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

Compare commits

..

22 Commits

Author SHA1 Message Date
n1474335
862cfdf0ae 10.10.0 2024-03-27 18:29:13 +00:00
n1474335
943d01c208 Updated CHANGELOG 2024-03-27 18:28:41 +00:00
n1474335
ef59634c15 Added 'JA4 Fingerprint' operation 2024-03-27 18:02:17 +00:00
n1474335
0026d77b7b More test tweaking 2024-03-26 16:34:36 +00:00
n1474335
ee77e0a1e4 More test tweaking 2024-03-26 16:10:44 +00:00
n1474335
f1dcc339b3 More test tweaking 2024-03-26 15:42:21 +00:00
n1474335
1f316a2f32 More test tweaking 2024-03-26 15:37:18 +00:00
n1474335
a5f9a8726b Fixed erroring test 2024-03-26 15:19:35 +00:00
n1474335
64111b8b7b Downgrade chromedriver version for GitHub Actions 2024-03-26 14:57:58 +00:00
n1474335
762cf3ca41 10.9.0 2024-03-26 14:40:39 +00:00
n1474335
70ff3a52ca Updated CHANGELOG 2024-03-26 14:40:33 +00:00
n1474335
e4077fb63b Lint and dependency update 2024-03-26 14:38:27 +00:00
n1474335
65ffd8d65d Automatically detect UTF8 character encoding in output 2024-03-26 13:44:59 +00:00
n1474335
16dfb3fac6 Automatically detect EOL from paste events and output setting 2024-03-26 13:44:58 +00:00
a3957273
c4e7c41a6e Merge pull request #501 from kassi/fernet
Add Fernet encryption/decryption operation
2024-02-22 01:15:24 +00:00
a3957273
210186e754 Fix tests 2024-02-22 01:00:11 +00:00
a3957273
b4c14219b6 Fix encrypt 2024-02-22 00:42:30 +00:00
a3957273
299a3c48a1 Update imports from Fernet module 2024-02-22 00:26:32 +00:00
a3957273
cd0aee7626 Remove deprecated code 2024-02-22 00:22:19 +00:00
a3957273
bc82f590d4 Merge branch 'master' into fernet 2024-02-22 00:20:40 +00:00
Karsten Silkenbäumer
55cac17456 Change author URL 2019-03-03 17:19:07 +01:00
Karsten Silkenbäumer
846e84d3a4 Add fernet encryption/decryption operation 2019-03-03 16:41:00 +01:00
27 changed files with 2017 additions and 210 deletions

View File

@@ -13,6 +13,12 @@ All major and minor version changes will be documented in this file. Details of
## Details
### [10.10.0] - 2024-03-27
- Added 'JA4 Fingerprint' operation [@n1474335] | [#1759]
### [10.9.0] - 2024-03-26
- Line ending sequences and UTF-8 character encoding are now detected automatically [@n1474335] | [65ffd8d]
### [10.8.0] - 2024-02-13
- Add official Docker images [@AshCorr] | [#1699]
@@ -386,6 +392,8 @@ All major and minor version changes will be documented in this file. Details of
## [4.0.0] - 2016-11-28
- Initial open source commit [@n1474335] | [b1d73a72](https://github.com/gchq/CyberChef/commit/b1d73a725dc7ab9fb7eb789296efd2b7e4b08306)
[10.10.0]: https://github.com/gchq/CyberChef/releases/tag/v10.10.0
[10.9.0]: https://github.com/gchq/CyberChef/releases/tag/v10.9.0
[10.8.0]: https://github.com/gchq/CyberChef/releases/tag/v10.7.0
[10.7.0]: https://github.com/gchq/CyberChef/releases/tag/v10.7.0
[10.6.0]: https://github.com/gchq/CyberChef/releases/tag/v10.6.0
@@ -561,6 +569,7 @@ All major and minor version changes will be documented in this file. Details of
[a895d1d]: https://github.com/gchq/CyberChef/commit/a895d1d82a2f92d440a0c5eca2bc7c898107b737
[31a7f83]: https://github.com/gchq/CyberChef/commit/31a7f83b82e78927f89689f323fcb9185144d6ff
[760eff4]: https://github.com/gchq/CyberChef/commit/760eff49b5307aaa3104c5e5b437ffe62299acd1
[65ffd8d]: https://github.com/gchq/CyberChef/commit/65ffd8d65d88eb369f6f61a5d1d0f807179bffb7
[#95]: https://github.com/gchq/CyberChef/pull/299
[#173]: https://github.com/gchq/CyberChef/pull/173

336
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "cyberchef",
"version": "10.8.2",
"version": "10.10.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "cyberchef",
"version": "10.8.2",
"version": "10.10.0",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@@ -40,6 +40,7 @@
"escodegen": "^2.1.0",
"esprima": "^4.0.1",
"exif-parser": "^0.1.12",
"fernet": "^0.3.2",
"file-saver": "^2.0.5",
"flat": "^6.0.1",
"geodesy": "1.1.3",
@@ -113,7 +114,7 @@
"babel-plugin-dynamic-import-node": "^2.3.3",
"babel-plugin-transform-builtin-extend": "1.1.2",
"base64-loader": "^1.0.0",
"chromedriver": "^121.0.0",
"chromedriver": "^122.0.0",
"cli-progress": "^3.12.0",
"colors": "^1.4.0",
"copy-webpack-plugin": "^12.0.2",
@@ -2829,6 +2830,12 @@
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
},
"node_modules/@tootallnate/quickjs-emscripten": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
"integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
"dev": true
},
"node_modules/@types/body-parser": {
"version": "1.19.2",
"dev": true,
@@ -3241,15 +3248,15 @@
}
},
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz",
"integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==",
"dev": true,
"dependencies": {
"debug": "4"
"debug": "^4.3.4"
},
"engines": {
"node": ">= 6.0.0"
"node": ">= 14"
}
},
"node_modules/ajv": {
@@ -3600,6 +3607,18 @@
"node": "*"
}
},
"node_modules/ast-types": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
"integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==",
"dev": true,
"dependencies": {
"tslib": "^2.0.1"
},
"engines": {
"node": ">=4"
}
},
"node_modules/async": {
"version": "3.2.3",
"dev": true,
@@ -3975,6 +3994,15 @@
"node": ">= 0.8"
}
},
"node_modules/basic-ftp": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
"integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==",
"dev": true,
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/batch": {
"version": "0.6.1",
"dev": true,
@@ -4707,17 +4735,17 @@
}
},
"node_modules/chromedriver": {
"version": "121.0.0",
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-121.0.0.tgz",
"integrity": "sha512-ZIKEdZrQAfuzT/RRofjl8/EZR99ghbdBXNTOcgJMKGP6N/UL6lHUX4n6ONWBV18pDvDFfQJ0x58h5AdOaXIOMw==",
"version": "122.0.6",
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-122.0.6.tgz",
"integrity": "sha512-Q0r+QlUtiJWMQ5HdYaFa0CtBmLFq3n5JWfmq9mOC00UMBvWxku09gUkvBt457QnYfTM/XHqY/HTFOxHvATnTmA==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"@testim/chrome-version": "^1.1.4",
"axios": "^1.6.5",
"axios": "^1.6.7",
"compare-versions": "^6.1.0",
"extract-zip": "^2.0.1",
"https-proxy-agent": "^5.0.1",
"proxy-agent": "^6.4.0",
"proxy-from-env": "^1.1.0",
"tcp-port-used": "^1.0.2"
},
@@ -5816,6 +5844,15 @@
"node": ">=12"
}
},
"node_modules/data-uri-to-buffer": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
"integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==",
"dev": true,
"engines": {
"node": ">= 14"
}
},
"node_modules/data-urls": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
@@ -5974,6 +6011,20 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/degenerator": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz",
"integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==",
"dev": true,
"dependencies": {
"ast-types": "^0.13.4",
"escodegen": "^2.1.0",
"esprima": "^4.0.1"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/delaunator": {
"version": "5.0.0",
"license": "ISC",
@@ -7100,6 +7151,15 @@
"pend": "~1.2.0"
}
},
"node_modules/fernet": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/fernet/-/fernet-0.3.2.tgz",
"integrity": "sha512-VPwO4hF9sp8YrCeiOjMb4HTg5WV5VC7Nk2EG3pfotqW9ZHa3aNnR+oGiOZu8k0Jp4VxJi0RTJwHmloyjWs+Mzg==",
"dependencies": {
"crypto-js": "~4.2.0",
"urlsafe-base64": "1.0.0"
}
},
"node_modules/file-entry-cache": {
"version": "6.0.1",
"dev": true,
@@ -7398,6 +7458,29 @@
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
"dev": true
},
"node_modules/fs-extra": {
"version": "11.2.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
"integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=14.14"
}
},
"node_modules/fs-extra/node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/fs-monkey": {
"version": "1.0.3",
"dev": true,
@@ -7501,6 +7584,21 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/get-uri": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz",
"integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==",
"dev": true,
"dependencies": {
"basic-ftp": "^5.0.2",
"data-uri-to-buffer": "^6.0.2",
"debug": "^4.3.4",
"fs-extra": "^11.2.0"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/getobject": {
"version": "1.0.2",
"dev": true,
@@ -8593,9 +8691,9 @@
}
},
"node_modules/http-proxy-agent": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz",
"integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==",
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
"dev": true,
"dependencies": {
"agent-base": "^7.1.0",
@@ -8605,18 +8703,6 @@
"node": ">= 14"
}
},
"node_modules/http-proxy-agent/node_modules/agent-base": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz",
"integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==",
"dev": true,
"dependencies": {
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/http-proxy-middleware": {
"version": "2.0.4",
"dev": true,
@@ -8657,16 +8743,16 @@
"license": "MIT"
},
"node_modules/https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz",
"integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==",
"dev": true,
"dependencies": {
"agent-base": "6",
"agent-base": "^7.0.2",
"debug": "4"
},
"engines": {
"node": ">= 6"
"node": ">= 14"
}
},
"node_modules/human-signals": {
@@ -8865,6 +8951,19 @@
"loose-envify": "^1.0.0"
}
},
"node_modules/ip-address": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
"integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==",
"dev": true,
"dependencies": {
"jsbn": "1.1.0",
"sprintf-js": "^1.1.3"
},
"engines": {
"node": ">= 12"
}
},
"node_modules/ip-regex": {
"version": "4.3.0",
"dev": true,
@@ -9492,6 +9591,12 @@
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/jsbn": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==",
"dev": true
},
"node_modules/jsdom": {
"version": "23.2.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-23.2.0.tgz",
@@ -9532,31 +9637,6 @@
}
}
},
"node_modules/jsdom/node_modules/agent-base": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz",
"integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==",
"dev": true,
"dependencies": {
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/jsdom/node_modules/https-proxy-agent": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz",
"integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==",
"dev": true,
"dependencies": {
"agent-base": "^7.0.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/jsesc": {
"version": "3.0.2",
"license": "MIT",
@@ -9593,6 +9673,27 @@
"node": ">=6"
}
},
"node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/jsonfile/node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/jsonpath-plus": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-8.0.0.tgz",
@@ -10686,6 +10787,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/netmask": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
"integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==",
"dev": true,
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/ngeohash": {
"version": "0.6.3",
"license": "MIT",
@@ -11478,6 +11588,38 @@
"node": ">= 4"
}
},
"node_modules/pac-proxy-agent": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz",
"integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==",
"dev": true,
"dependencies": {
"@tootallnate/quickjs-emscripten": "^0.23.0",
"agent-base": "^7.0.2",
"debug": "^4.3.4",
"get-uri": "^6.0.1",
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.2",
"pac-resolver": "^7.0.0",
"socks-proxy-agent": "^8.0.2"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/pac-resolver": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz",
"integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==",
"dev": true,
"dependencies": {
"degenerator": "^5.0.0",
"netmask": "^2.0.2"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/pad-stream": {
"version": "2.0.0",
"dev": true,
@@ -12249,6 +12391,34 @@
"node": ">= 0.10"
}
},
"node_modules/proxy-agent": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz",
"integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==",
"dev": true,
"dependencies": {
"agent-base": "^7.0.2",
"debug": "^4.3.4",
"http-proxy-agent": "^7.0.1",
"https-proxy-agent": "^7.0.3",
"lru-cache": "^7.14.1",
"pac-proxy-agent": "^7.0.1",
"proxy-from-env": "^1.1.0",
"socks-proxy-agent": "^8.0.2"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/proxy-agent/node_modules/lru-cache": {
"version": "7.18.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
"dev": true,
"engines": {
"node": ">=12"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@@ -13182,6 +13352,16 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/smart-buffer": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
"dev": true,
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/snackbarjs": {
"version": "1.1.0",
"license": "ISC"
@@ -13207,6 +13387,34 @@
"node": ">=0.8.0"
}
},
"node_modules/socks": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.1.tgz",
"integrity": "sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ==",
"dev": true,
"dependencies": {
"ip-address": "^9.0.5",
"smart-buffer": "^4.2.0"
},
"engines": {
"node": ">= 10.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/socks-proxy-agent": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz",
"integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==",
"dev": true,
"dependencies": {
"agent-base": "^7.0.2",
"debug": "^4.3.4",
"socks": "^2.7.1"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/sortablejs": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.2.tgz",
@@ -13278,9 +13486,10 @@
}
},
"node_modules/sprintf-js": {
"version": "1.1.2",
"dev": true,
"license": "BSD-3-Clause"
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
"dev": true
},
"node_modules/ssdeep.js": {
"version": "0.0.3"
@@ -14081,6 +14290,11 @@
"dev": true,
"license": "MIT"
},
"node_modules/urlsafe-base64": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/urlsafe-base64/-/urlsafe-base64-1.0.0.tgz",
"integrity": "sha512-RtuPeMy7c1UrHwproMZN9gN6kiZ0SvJwRaEzwZY0j9MypEkFqyBaKv176jvlPtg58Zh36bOkS0NFABXMHvvGCA=="
},
"node_modules/utf8": {
"version": "3.0.0",
"license": "MIT"

View File

@@ -1,6 +1,6 @@
{
"name": "cyberchef",
"version": "10.8.2",
"version": "10.10.0",
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
"author": "n1474335 <n1474335@gmail.com>",
"homepage": "https://gchq.github.io/CyberChef",
@@ -55,7 +55,7 @@
"babel-plugin-dynamic-import-node": "^2.3.3",
"babel-plugin-transform-builtin-extend": "1.1.2",
"base64-loader": "^1.0.0",
"chromedriver": "^121.0.0",
"chromedriver": "^122.0.0",
"cli-progress": "^3.12.0",
"colors": "^1.4.0",
"copy-webpack-plugin": "^12.0.2",
@@ -122,6 +122,7 @@
"escodegen": "^2.1.0",
"esprima": "^4.0.1",
"exif-parser": "^0.1.12",
"fernet": "^0.3.2",
"file-saver": "^2.0.5",
"flat": "^6.0.1",
"geodesy": "1.1.3",

View File

@@ -86,6 +86,8 @@
"DES Decrypt",
"Triple DES Encrypt",
"Triple DES Decrypt",
"Fernet Encrypt",
"Fernet Decrypt",
"LS47 Encrypt",
"LS47 Decrypt",
"RC2 Encrypt",
@@ -229,6 +231,7 @@
"VarInt Decode",
"JA3 Fingerprint",
"JA3S Fingerprint",
"JA4 Fingerprint",
"HASSH Client Fingerprint",
"HASSH Server Fingerprint",
"Format MAC addresses",

View File

@@ -224,8 +224,85 @@ export function chrEncWidth(page) {
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
export const UNICODE_NORMALISATION_FORMS = ["NFD", "NFC", "NFKD", "NFKC"];
/**
* Character encoding format mappings.
* Detects whether the input buffer is valid UTF8.
*
* @param {ArrayBuffer} data
* @returns {number} - 0 = not UTF8, 1 = ASCII, 2 = UTF8
*/
export const UNICODE_NORMALISATION_FORMS = ["NFD", "NFC", "NFKD", "NFKC"];
export function isUTF8(data) {
const bytes = new Uint8Array(data);
let i = 0;
let onlyASCII = true;
while (i < bytes.length) {
if (( // ASCII
bytes[i] === 0x09 ||
bytes[i] === 0x0A ||
bytes[i] === 0x0D ||
(0x20 <= bytes[i] && bytes[i] <= 0x7E)
)) {
i += 1;
continue;
}
onlyASCII = false;
if (( // non-overlong 2-byte
(0xC2 <= bytes[i] && bytes[i] <= 0xDF) &&
(0x80 <= bytes[i+1] && bytes[i+1] <= 0xBF)
)) {
i += 2;
continue;
}
if (( // excluding overlongs
bytes[i] === 0xE0 &&
(0xA0 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) &&
(0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF)
) ||
( // straight 3-byte
((0xE1 <= bytes[i] && bytes[i] <= 0xEC) ||
bytes[i] === 0xEE ||
bytes[i] === 0xEF) &&
(0x80 <= bytes[i + 1] && bytes[i+1] <= 0xBF) &&
(0x80 <= bytes[i+2] && bytes[i+2] <= 0xBF)
) ||
( // excluding surrogates
bytes[i] === 0xED &&
(0x80 <= bytes[i+1] && bytes[i+1] <= 0x9F) &&
(0x80 <= bytes[i+2] && bytes[i+2] <= 0xBF)
)) {
i += 3;
continue;
}
if (( // planes 1-3
bytes[i] === 0xF0 &&
(0x90 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) &&
(0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) &&
(0x80 <= bytes[i + 3] && bytes[i + 3] <= 0xBF)
) ||
( // planes 4-15
(0xF1 <= bytes[i] && bytes[i] <= 0xF3) &&
(0x80 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) &&
(0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) &&
(0x80 <= bytes[i + 3] && bytes[i + 3] <= 0xBF)
) ||
( // plane 16
bytes[i] === 0xF4 &&
(0x80 <= bytes[i + 1] && bytes[i + 1] <= 0x8F) &&
(0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) &&
(0x80 <= bytes[i + 3] && bytes[i + 3] <= 0xBF)
)) {
i += 4;
continue;
}
return 0;
}
return onlyASCII ? 1 : 2;
}

166
src/core/lib/JA4.mjs Normal file
View File

@@ -0,0 +1,166 @@
/**
* JA4 resources.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2024
* @license Apache-2.0
*
* JA4 Copyright 2023 FoxIO, LLC.
* @license BSD-3-Clause
*/
import OperationError from "../errors/OperationError.mjs";
import { parseTLSRecord, parseHighestSupportedVersion, parseFirstALPNValue } from "./TLS.mjs";
import { toHexFast } from "./Hex.mjs";
import { runHash } from "./Hash.mjs";
import Utils from "../Utils.mjs";
/**
* Calculate the JA4 from a given TLS Client Hello Stream
* @param {Uint8Array} bytes
* @returns {string}
*/
export function toJA4(bytes) {
let tlsr = {};
try {
tlsr = parseTLSRecord(bytes);
} catch (err) {
throw new OperationError("Data is not a valid TLS Client Hello. QUIC is not yet supported.\n" + err);
}
/* QUIC
“q” or “t”, which denotes whether the hello packet is for QUIC or TCP.
TODO: Implement QUIC
*/
const ptype = "t";
/* TLS Version
TLS version is shown in 3 different places. If extension 0x002b exists (supported_versions), then the version
is the highest value in the extension. Remember to ignore GREASE values. If the extension doesnt exist, then
the TLS version is the value of the Protocol Version. Handshake version (located at the top of the packet)
should be ignored.
*/
let version = tlsr.version.value;
for (const ext of tlsr.handshake.value.extensions.value) {
if (ext.type.value === "supported_versions") {
version = parseHighestSupportedVersion(ext.value.data);
break;
}
}
switch (version) {
case 0x0304: version = "13"; break; // TLS 1.3
case 0x0303: version = "12"; break; // TLS 1.2
case 0x0302: version = "11"; break; // TLS 1.1
case 0x0301: version = "10"; break; // TLS 1.0
case 0x0300: version = "s3"; break; // SSL 3.0
case 0x0200: version = "s2"; break; // SSL 2.0
case 0x0100: version = "s1"; break; // SSL 1.0
default: version = "00"; // Unknown
}
/* SNI
If the SNI extension (0x0000) exists, then the destination of the connection is a domain, or “d” in the fingerprint.
If the SNI does not exist, then the destination is an IP address, or “i”.
*/
let sni = "i";
for (const ext of tlsr.handshake.value.extensions.value) {
if (ext.type.value === "server_name") {
sni = "d";
break;
}
}
/* Number of Ciphers
2 character number of cipher suites, so if theres 6 cipher suites in the hello packet, then the value should be “06”.
If theres > 99, which there should never be, then output “99”. Remember, ignore GREASE values. They dont count.
*/
let cipherLen = 0;
for (const cs of tlsr.handshake.value.cipherSuites.value) {
if (cs.value !== "GREASE") cipherLen++;
}
cipherLen = cipherLen > 99 ? "99" : cipherLen.toString().padStart(2, "0");
/* Number of Extensions
Same as counting ciphers. Ignore GREASE. Include SNI and ALPN.
*/
let extLen = 0;
for (const ext of tlsr.handshake.value.extensions.value) {
if (ext.type.value !== "GREASE") extLen++;
}
extLen = extLen > 99 ? "99" : extLen.toString().padStart(2, "0");
/* ALPN Extension Value
The first and last characters of the ALPN (Application-Layer Protocol Negotiation) first value.
If there are no ALPN values or no ALPN extension then we print “00” as the value in the fingerprint.
*/
let alpn = "00";
for (const ext of tlsr.handshake.value.extensions.value) {
if (ext.type.value === "application_layer_protocol_negotiation") {
alpn = parseFirstALPNValue(ext.value.data);
alpn = alpn.charAt(0) + alpn.charAt(alpn.length - 1);
break;
}
}
/* Cipher hash
A 12 character truncated sha256 hash of the list of ciphers sorted in hex order, first 12 characters.
The list is created using the 4 character hex values of the ciphers, lower case, comma delimited, ignoring GREASE.
*/
const originalCiphersList = [];
for (const cs of tlsr.handshake.value.cipherSuites.value) {
if (cs.value !== "GREASE") {
originalCiphersList.push(toHexFast(cs.data));
}
}
const sortedCiphersList = [...originalCiphersList].sort();
const sortedCiphersRaw = sortedCiphersList.join(",");
const originalCiphersRaw = originalCiphersList.join(",");
const sortedCiphers = runHash(
"sha256",
Utils.strToArrayBuffer(sortedCiphersRaw)
).substring(0, 12);
const originalCiphers = runHash(
"sha256",
Utils.strToArrayBuffer(originalCiphersRaw)
).substring(0, 12);
/* Extension hash
A 12 character truncated sha256 hash of the list of extensions, sorted by hex value, followed by the list of signature
algorithms, in the order that they appear (not sorted).
The extension list is created using the 4 character hex values of the extensions, lower case, comma delimited, sorted
(not in the order they appear). Ignore the SNI extension (0000) and the ALPN extension (0010) as weve already captured
them in the a section of the fingerprint. These values are omitted so that the same application would have the same b
section of the fingerprint regardless of if it were going to a domain, IP, or changing ALPNs.
*/
const originalExtensionsList = [];
let signatureAlgorithms = "";
for (const ext of tlsr.handshake.value.extensions.value) {
if (ext.type.value !== "GREASE") {
originalExtensionsList.push(toHexFast(ext.type.data));
}
if (ext.type.value === "signature_algorithms") {
signatureAlgorithms = toHexFast(ext.value.data.slice(2));
signatureAlgorithms = signatureAlgorithms.replace(/(.{4})/g, "$1,");
signatureAlgorithms = signatureAlgorithms.substring(0, signatureAlgorithms.length - 1);
}
}
const sortedExtensionsList = [...originalExtensionsList].filter(e => e !== "0000" && e !== "0010").sort();
const sortedExtensionsRaw = sortedExtensionsList.join(",") + "_" + signatureAlgorithms;
const originalExtensionsRaw = originalExtensionsList.join(",") + "_" + signatureAlgorithms;
const sortedExtensions = runHash(
"sha256",
Utils.strToArrayBuffer(sortedExtensionsRaw)
).substring(0, 12);
const originalExtensions = runHash(
"sha256",
Utils.strToArrayBuffer(originalExtensionsRaw)
).substring(0, 12);
return {
"JA4": `${ptype}${version}${sni}${cipherLen}${extLen}${alpn}_${sortedCiphers}_${sortedExtensions}`,
"JA4_o": `${ptype}${version}${sni}${cipherLen}${extLen}${alpn}_${originalCiphers}_${originalExtensions}`,
"JA4_r": `${ptype}${version}${sni}${cipherLen}${extLen}${alpn}_${sortedCiphersRaw}_${sortedExtensionsRaw}`,
"JA4_ro": `${ptype}${version}${sni}${cipherLen}${extLen}${alpn}_${originalCiphersRaw}_${originalExtensionsRaw}`,
};
}

View File

@@ -3,6 +3,7 @@ import Utils, { isWorkerEnvironment } from "../Utils.mjs";
import Recipe from "../Recipe.mjs";
import Dish from "../Dish.mjs";
import {detectFileType, isType} from "./FileType.mjs";
import {isUTF8} from "./ChrEnc.mjs";
import chiSquared from "chi-squared";
/**
@@ -111,82 +112,6 @@ class Magic {
};
}
/**
* Detects whether the input buffer is valid UTF8.
*
* @returns {boolean}
*/
isUTF8() {
const bytes = new Uint8Array(this.inputBuffer);
let i = 0;
while (i < bytes.length) {
if (( // ASCII
bytes[i] === 0x09 ||
bytes[i] === 0x0A ||
bytes[i] === 0x0D ||
(0x20 <= bytes[i] && bytes[i] <= 0x7E)
)) {
i += 1;
continue;
}
if (( // non-overlong 2-byte
(0xC2 <= bytes[i] && bytes[i] <= 0xDF) &&
(0x80 <= bytes[i+1] && bytes[i+1] <= 0xBF)
)) {
i += 2;
continue;
}
if (( // excluding overlongs
bytes[i] === 0xE0 &&
(0xA0 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) &&
(0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF)
) ||
( // straight 3-byte
((0xE1 <= bytes[i] && bytes[i] <= 0xEC) ||
bytes[i] === 0xEE ||
bytes[i] === 0xEF) &&
(0x80 <= bytes[i + 1] && bytes[i+1] <= 0xBF) &&
(0x80 <= bytes[i+2] && bytes[i+2] <= 0xBF)
) ||
( // excluding surrogates
bytes[i] === 0xED &&
(0x80 <= bytes[i+1] && bytes[i+1] <= 0x9F) &&
(0x80 <= bytes[i+2] && bytes[i+2] <= 0xBF)
)) {
i += 3;
continue;
}
if (( // planes 1-3
bytes[i] === 0xF0 &&
(0x90 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) &&
(0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) &&
(0x80 <= bytes[i + 3] && bytes[i + 3] <= 0xBF)
) ||
( // planes 4-15
(0xF1 <= bytes[i] && bytes[i] <= 0xF3) &&
(0x80 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) &&
(0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) &&
(0x80 <= bytes[i + 3] && bytes[i + 3] <= 0xBF)
) ||
( // plane 16
bytes[i] === 0xF4 &&
(0x80 <= bytes[i + 1] && bytes[i + 1] <= 0x8F) &&
(0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) &&
(0x80 <= bytes[i + 3] && bytes[i + 3] <= 0xBF)
)) {
i += 4;
continue;
}
return false;
}
return true;
}
/**
* Calculates the Shannon entropy of the input data.
*
@@ -336,7 +261,7 @@ class Magic {
data: this.inputStr.slice(0, 100),
languageScores: this.detectLanguage(extLang),
fileType: this.detectFileType(),
isUTF8: this.isUTF8(),
isUTF8: !!isUTF8(this.inputBuffer),
entropy: this.calcEntropy(),
matchingOps: matchingOps,
useful: useful,

View File

@@ -18,12 +18,23 @@ export default class Stream {
* Stream constructor.
*
* @param {Uint8Array} input
* @param {number} pos
* @param {number} bitPos
*/
constructor(input) {
constructor(input, pos=0, bitPos=0) {
this.bytes = input;
this.length = this.bytes.length;
this.position = 0;
this.bitPos = 0;
this.position = pos;
this.bitPos = bitPos;
}
/**
* Clone this Stream returning a new identical Stream.
*
* @returns {Stream}
*/
clone() {
return new Stream(this.bytes, this.position, this.bitPos);
}
/**

776
src/core/lib/TLS.mjs Normal file
View File

@@ -0,0 +1,776 @@
/**
* TLS resources.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import OperationError from "../errors/OperationError.mjs";
import Stream from "../lib/Stream.mjs";
/**
* Parse a TLS Record
* @param {Uint8Array} bytes
* @returns {JSON}
*/
export function parseTLSRecord(bytes) {
const s = new Stream(bytes);
const b = s.clone();
const r = {};
// Content type
r.contentType = {
description: "Content Type",
length: 1,
data: b.getBytes(1),
value: s.readInt(1)
};
if (r.contentType.value !== 0x16)
throw new OperationError("Not handshake data.");
// Version
r.version = {
description: "Protocol Version",
length: 2,
data: b.getBytes(2),
value: s.readInt(2)
};
// Length
r.length = {
description: "Record Length",
length: 2,
data: b.getBytes(2),
value: s.readInt(2)
};
if (s.length !== r.length.value + 5)
throw new OperationError("Incorrect handshake length.");
// Handshake
r.handshake = {
description: "Handshake",
length: r.length.value,
data: b.getBytes(r.length.value),
value: parseHandshake(s.getBytes(r.length.value))
};
return r;
}
/**
* Parse a TLS Handshake
* @param {Uint8Array} bytes
* @returns {JSON}
*/
function parseHandshake(bytes) {
const s = new Stream(bytes);
const b = s.clone();
const h = {};
// Handshake type
h.handshakeType = {
description: "Client Hello",
length: 1,
data: b.getBytes(1),
value: s.readInt(1)
};
if (h.handshakeType.value !== 0x01)
throw new OperationError("Not a Client Hello.");
// Handshake length
h.handshakeLength = {
description: "Handshake Length",
length: 3,
data: b.getBytes(3),
value: s.readInt(3)
};
if (s.length !== h.handshakeLength.value + 4)
throw new OperationError("Not enough data in Client Hello.");
// Hello version
h.helloVersion = {
description: "Client Hello Version",
length: 2,
data: b.getBytes(2),
value: s.readInt(2)
};
// Random
h.random = {
description: "Client Random",
length: 32,
data: b.getBytes(32),
value: s.getBytes(32)
};
// Session ID Length
h.sessionIDLength = {
description: "Session ID Length",
length: 1,
data: b.getBytes(1),
value: s.readInt(1)
};
// Session ID
h.sessionID = {
description: "Session ID",
length: h.sessionIDLength.value,
data: b.getBytes(h.sessionIDLength.value),
value: s.getBytes(h.sessionIDLength.value)
};
// Cipher Suites Length
h.cipherSuitesLength = {
description: "Cipher Suites Length",
length: 2,
data: b.getBytes(2),
value: s.readInt(2)
};
// Cipher Suites
h.cipherSuites = {
description: "Cipher Suites",
length: h.cipherSuitesLength.value,
data: b.getBytes(h.cipherSuitesLength.value),
value: parseCipherSuites(s.getBytes(h.cipherSuitesLength.value))
};
// Compression Methods Length
h.compressionMethodsLength = {
description: "Compression Methods Length",
length: 1,
data: b.getBytes(1),
value: s.readInt(1)
};
// Compression Methods
h.compressionMethods = {
description: "Compression Methods",
length: h.compressionMethodsLength.value,
data: b.getBytes(h.compressionMethodsLength.value),
value: parseCompressionMethods(s.getBytes(h.compressionMethodsLength.value))
};
// Extensions Length
h.extensionsLength = {
description: "Extensions Length",
length: 2,
data: b.getBytes(2),
value: s.readInt(2)
};
// Extensions
h.extensions = {
description: "Extensions",
length: h.extensionsLength.value,
data: b.getBytes(h.extensionsLength.value),
value: parseExtensions(s.getBytes(h.extensionsLength.value))
};
return h;
}
/**
* Parse Cipher Suites
* @param {Uint8Array} bytes
* @returns {JSON}
*/
function parseCipherSuites(bytes) {
const s = new Stream(bytes);
const b = s.clone();
const cs = [];
while (s.hasMore()) {
cs.push({
description: "Cipher Suite",
length: 2,
data: b.getBytes(2),
value: CIPHER_SUITES_LOOKUP[s.readInt(2)] || "Unknown"
});
}
return cs;
}
/**
* Parse Compression Methods
* @param {Uint8Array} bytes
* @returns {JSON}
*/
function parseCompressionMethods(bytes) {
const s = new Stream(bytes);
const b = s.clone();
const cm = [];
while (s.hasMore()) {
cm.push({
description: "Compression Method",
length: 1,
data: b.getBytes(1),
value: s.readInt(1) // TODO: Compression method name here
});
}
return cm;
}
/**
* Parse Extensions
* @param {Uint8Array} bytes
* @returns {JSON}
*/
function parseExtensions(bytes) {
const s = new Stream(bytes);
const b = s.clone();
const exts = [];
while (s.hasMore()) {
const ext = {};
// Type
ext.type = {
description: "Extension Type",
length: 2,
data: b.getBytes(2),
value: EXTENSION_LOOKUP[s.readInt(2)] || "unknown"
};
// Length
ext.length = {
description: "Extension Length",
length: 2,
data: b.getBytes(2),
value: s.readInt(2)
};
// Value
ext.value = {
description: "Extension Value",
length: ext.length.value,
data: b.getBytes(ext.length.value),
value: s.getBytes(ext.length.value)
};
exts.push(ext);
}
return exts;
}
/**
* Extension type lookup table
*/
const EXTENSION_LOOKUP = {
0: "server_name",
1: "max_fragment_length",
2: "client_certificate_url",
3: "trusted_ca_keys",
4: "truncated_hmac",
5: "status_request",
6: "user_mapping",
7: "client_authz",
8: "server_authz",
9: "cert_type",
10: "supported_groups",
11: "ec_point_formats",
12: "srp",
13: "signature_algorithms",
14: "use_srtp",
15: "heartbeat",
16: "application_layer_protocol_negotiation",
17: "status_request_v2",
18: "signed_certificate_timestamp",
19: "client_certificate_type",
20: "server_certificate_type",
21: "padding",
22: "encrypt_then_mac",
23: "extended_master_secret",
24: "token_binding",
25: "cached_info",
26: "tls_lts",
27: "compress_certificate",
28: "record_size_limit",
29: "pwd_protect",
30: "pwd_clear",
31: "password_salt",
32: "ticket_pinning",
33: "tls_cert_with_extern_psk",
34: "delegated_credential",
35: "session_ticket",
36: "TLMSP",
37: "TLMSP_proxying",
38: "TLMSP_delegate",
39: "supported_ekt_ciphers",
40: "Reserved",
41: "pre_shared_key",
42: "early_data",
43: "supported_versions",
44: "cookie",
45: "psk_key_exchange_modes",
46: "Reserved",
47: "certificate_authorities",
48: "oid_filters",
49: "post_handshake_auth",
50: "signature_algorithms_cert",
51: "key_share",
52: "transparency_info",
53: "connection_id (deprecated)",
54: "connection_id",
55: "external_id_hash",
56: "external_session_id",
57: "quic_transport_parameters",
58: "ticket_request",
59: "dnssec_chain",
60: "sequence_number_encryption_algorithms",
61: "rrc",
2570: "GREASE",
6682: "GREASE",
10794: "GREASE",
14906: "GREASE",
17513: "application_settings",
19018: "GREASE",
23130: "GREASE",
27242: "GREASE",
31354: "GREASE",
35466: "GREASE",
39578: "GREASE",
43690: "GREASE",
47802: "GREASE",
51914: "GREASE",
56026: "GREASE",
60138: "GREASE",
64250: "GREASE",
64768: "ech_outer_extensions",
65037: "encrypted_client_hello",
65281: "renegotiation_info"
};
/**
* Cipher suites lookup table
*/
const CIPHER_SUITES_LOOKUP = {
0x0000: "TLS_NULL_WITH_NULL_NULL",
0x0001: "TLS_RSA_WITH_NULL_MD5",
0x0002: "TLS_RSA_WITH_NULL_SHA",
0x0003: "TLS_RSA_EXPORT_WITH_RC4_40_MD5",
0x0004: "TLS_RSA_WITH_RC4_128_MD5",
0x0005: "TLS_RSA_WITH_RC4_128_SHA",
0x0006: "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5",
0x0007: "TLS_RSA_WITH_IDEA_CBC_SHA",
0x0008: "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA",
0x0009: "TLS_RSA_WITH_DES_CBC_SHA",
0x000A: "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
0x000B: "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA",
0x000C: "TLS_DH_DSS_WITH_DES_CBC_SHA",
0x000D: "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA",
0x000E: "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA",
0x000F: "TLS_DH_RSA_WITH_DES_CBC_SHA",
0x0010: "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA",
0x0011: "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
0x0012: "TLS_DHE_DSS_WITH_DES_CBC_SHA",
0x0013: "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
0x0014: "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
0x0015: "TLS_DHE_RSA_WITH_DES_CBC_SHA",
0x0016: "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
0x0017: "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5",
0x0018: "TLS_DH_anon_WITH_RC4_128_MD5",
0x0019: "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
0x001A: "TLS_DH_anon_WITH_DES_CBC_SHA",
0x001B: "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA",
0x001E: "TLS_KRB5_WITH_DES_CBC_SHA",
0x001F: "TLS_KRB5_WITH_3DES_EDE_CBC_SHA",
0x0020: "TLS_KRB5_WITH_RC4_128_SHA",
0x0021: "TLS_KRB5_WITH_IDEA_CBC_SHA",
0x0022: "TLS_KRB5_WITH_DES_CBC_MD5",
0x0023: "TLS_KRB5_WITH_3DES_EDE_CBC_MD5",
0x0024: "TLS_KRB5_WITH_RC4_128_MD5",
0x0025: "TLS_KRB5_WITH_IDEA_CBC_MD5",
0x0026: "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA",
0x0027: "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA",
0x0028: "TLS_KRB5_EXPORT_WITH_RC4_40_SHA",
0x0029: "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5",
0x002A: "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5",
0x002B: "TLS_KRB5_EXPORT_WITH_RC4_40_MD5",
0x002C: "TLS_PSK_WITH_NULL_SHA",
0x002D: "TLS_DHE_PSK_WITH_NULL_SHA",
0x002E: "TLS_RSA_PSK_WITH_NULL_SHA",
0x002F: "TLS_RSA_WITH_AES_128_CBC_SHA",
0x0030: "TLS_DH_DSS_WITH_AES_128_CBC_SHA",
0x0031: "TLS_DH_RSA_WITH_AES_128_CBC_SHA",
0x0032: "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
0x0033: "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
0x0034: "TLS_DH_anon_WITH_AES_128_CBC_SHA",
0x0035: "TLS_RSA_WITH_AES_256_CBC_SHA",
0x0036: "TLS_DH_DSS_WITH_AES_256_CBC_SHA",
0x0037: "TLS_DH_RSA_WITH_AES_256_CBC_SHA",
0x0038: "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
0x0039: "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
0x003A: "TLS_DH_anon_WITH_AES_256_CBC_SHA",
0x003B: "TLS_RSA_WITH_NULL_SHA256",
0x003C: "TLS_RSA_WITH_AES_128_CBC_SHA256",
0x003D: "TLS_RSA_WITH_AES_256_CBC_SHA256",
0x003E: "TLS_DH_DSS_WITH_AES_128_CBC_SHA256",
0x003F: "TLS_DH_RSA_WITH_AES_128_CBC_SHA256",
0x0040: "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
0x0041: "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA",
0x0042: "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA",
0x0043: "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA",
0x0044: "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA",
0x0045: "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA",
0x0046: "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA",
0x0067: "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
0x0068: "TLS_DH_DSS_WITH_AES_256_CBC_SHA256",
0x0069: "TLS_DH_RSA_WITH_AES_256_CBC_SHA256",
0x006A: "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
0x006B: "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
0x006C: "TLS_DH_anon_WITH_AES_128_CBC_SHA256",
0x006D: "TLS_DH_anon_WITH_AES_256_CBC_SHA256",
0x0084: "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA",
0x0085: "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA",
0x0086: "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA",
0x0087: "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA",
0x0088: "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA",
0x0089: "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA",
0x008A: "TLS_PSK_WITH_RC4_128_SHA",
0x008B: "TLS_PSK_WITH_3DES_EDE_CBC_SHA",
0x008C: "TLS_PSK_WITH_AES_128_CBC_SHA",
0x008D: "TLS_PSK_WITH_AES_256_CBC_SHA",
0x008E: "TLS_DHE_PSK_WITH_RC4_128_SHA",
0x008F: "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA",
0x0090: "TLS_DHE_PSK_WITH_AES_128_CBC_SHA",
0x0091: "TLS_DHE_PSK_WITH_AES_256_CBC_SHA",
0x0092: "TLS_RSA_PSK_WITH_RC4_128_SHA",
0x0093: "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA",
0x0094: "TLS_RSA_PSK_WITH_AES_128_CBC_SHA",
0x0095: "TLS_RSA_PSK_WITH_AES_256_CBC_SHA",
0x0096: "TLS_RSA_WITH_SEED_CBC_SHA",
0x0097: "TLS_DH_DSS_WITH_SEED_CBC_SHA",
0x0098: "TLS_DH_RSA_WITH_SEED_CBC_SHA",
0x0099: "TLS_DHE_DSS_WITH_SEED_CBC_SHA",
0x009A: "TLS_DHE_RSA_WITH_SEED_CBC_SHA",
0x009B: "TLS_DH_anon_WITH_SEED_CBC_SHA",
0x009C: "TLS_RSA_WITH_AES_128_GCM_SHA256",
0x009D: "TLS_RSA_WITH_AES_256_GCM_SHA384",
0x009E: "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
0x009F: "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
0x00A0: "TLS_DH_RSA_WITH_AES_128_GCM_SHA256",
0x00A1: "TLS_DH_RSA_WITH_AES_256_GCM_SHA384",
0x00A2: "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
0x00A3: "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
0x00A4: "TLS_DH_DSS_WITH_AES_128_GCM_SHA256",
0x00A5: "TLS_DH_DSS_WITH_AES_256_GCM_SHA384",
0x00A6: "TLS_DH_anon_WITH_AES_128_GCM_SHA256",
0x00A7: "TLS_DH_anon_WITH_AES_256_GCM_SHA384",
0x00A8: "TLS_PSK_WITH_AES_128_GCM_SHA256",
0x00A9: "TLS_PSK_WITH_AES_256_GCM_SHA384",
0x00AA: "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256",
0x00AB: "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384",
0x00AC: "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256",
0x00AD: "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384",
0x00AE: "TLS_PSK_WITH_AES_128_CBC_SHA256",
0x00AF: "TLS_PSK_WITH_AES_256_CBC_SHA384",
0x00B0: "TLS_PSK_WITH_NULL_SHA256",
0x00B1: "TLS_PSK_WITH_NULL_SHA384",
0x00B2: "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256",
0x00B3: "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384",
0x00B4: "TLS_DHE_PSK_WITH_NULL_SHA256",
0x00B5: "TLS_DHE_PSK_WITH_NULL_SHA384",
0x00B6: "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256",
0x00B7: "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384",
0x00B8: "TLS_RSA_PSK_WITH_NULL_SHA256",
0x00B9: "TLS_RSA_PSK_WITH_NULL_SHA384",
0x00BA: "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256",
0x00BB: "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256",
0x00BC: "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256",
0x00BD: "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256",
0x00BE: "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
0x00BF: "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256",
0x00C0: "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256",
0x00C1: "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256",
0x00C2: "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256",
0x00C3: "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256",
0x00C4: "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256",
0x00C5: "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256",
0x00C6: "TLS_SM4_GCM_SM3",
0x00C7: "TLS_SM4_CCM_SM3",
0x00FF: "TLS_EMPTY_RENEGOTIATION_INFO_SCSV",
0x0A0A: "GREASE",
0x1301: "TLS_AES_128_GCM_SHA256",
0x1302: "TLS_AES_256_GCM_SHA384",
0x1303: "TLS_CHACHA20_POLY1305_SHA256",
0x1304: "TLS_AES_128_CCM_SHA256",
0x1305: "TLS_AES_128_CCM_8_SHA256",
0x1306: "TLS_AEGIS_256_SHA512",
0x1307: "TLS_AEGIS_128L_SHA256",
0x1A1A: "GREASE",
0x2A2A: "GREASE",
0x3A3A: "GREASE",
0x4A4A: "GREASE",
0x5600: "TLS_FALLBACK_SCSV",
0x5A5A: "GREASE",
0x6A6A: "GREASE",
0x7A7A: "GREASE",
0x8A8A: "GREASE",
0x9A9A: "GREASE",
0xAAAA: "GREASE",
0xBABA: "GREASE",
0xC001: "TLS_ECDH_ECDSA_WITH_NULL_SHA",
0xC002: "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
0xC003: "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
0xC004: "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
0xC005: "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA",
0xC006: "TLS_ECDHE_ECDSA_WITH_NULL_SHA",
0xC007: "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
0xC008: "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
0xC009: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
0xC00A: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
0xC00B: "TLS_ECDH_RSA_WITH_NULL_SHA",
0xC00C: "TLS_ECDH_RSA_WITH_RC4_128_SHA",
0xC00D: "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
0xC00E: "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",
0xC00F: "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA",
0xC010: "TLS_ECDHE_RSA_WITH_NULL_SHA",
0xC011: "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
0xC012: "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
0xC013: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
0xC014: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
0xC015: "TLS_ECDH_anon_WITH_NULL_SHA",
0xC016: "TLS_ECDH_anon_WITH_RC4_128_SHA",
0xC017: "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA",
0xC018: "TLS_ECDH_anon_WITH_AES_128_CBC_SHA",
0xC019: "TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
0xC01A: "TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA",
0xC01B: "TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA",
0xC01C: "TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA",
0xC01D: "TLS_SRP_SHA_WITH_AES_128_CBC_SHA",
0xC01E: "TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA",
0xC01F: "TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA",
0xC020: "TLS_SRP_SHA_WITH_AES_256_CBC_SHA",
0xC021: "TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA",
0xC022: "TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA",
0xC023: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
0xC024: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
0xC025: "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",
0xC026: "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384",
0xC027: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
0xC028: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
0xC029: "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256",
0xC02A: "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384",
0xC02B: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
0xC02C: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
0xC02D: "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
0xC02E: "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384",
0xC02F: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
0xC030: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
0xC031: "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256",
0xC032: "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384",
0xC033: "TLS_ECDHE_PSK_WITH_RC4_128_SHA",
0xC034: "TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA",
0xC035: "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
0xC036: "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",
0xC037: "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256",
0xC038: "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384",
0xC039: "TLS_ECDHE_PSK_WITH_NULL_SHA",
0xC03A: "TLS_ECDHE_PSK_WITH_NULL_SHA256",
0xC03B: "TLS_ECDHE_PSK_WITH_NULL_SHA384",
0xC03C: "TLS_RSA_WITH_ARIA_128_CBC_SHA256",
0xC03D: "TLS_RSA_WITH_ARIA_256_CBC_SHA384",
0xC03E: "TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256",
0xC03F: "TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384",
0xC040: "TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256",
0xC041: "TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384",
0xC042: "TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256",
0xC043: "TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384",
0xC044: "TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256",
0xC045: "TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384",
0xC046: "TLS_DH_anon_WITH_ARIA_128_CBC_SHA256",
0xC047: "TLS_DH_anon_WITH_ARIA_256_CBC_SHA384",
0xC048: "TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256",
0xC049: "TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384",
0xC04A: "TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256",
0xC04B: "TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384",
0xC04C: "TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256",
0xC04D: "TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384",
0xC04E: "TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256",
0xC04F: "TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384",
0xC050: "TLS_RSA_WITH_ARIA_128_GCM_SHA256",
0xC051: "TLS_RSA_WITH_ARIA_256_GCM_SHA384",
0xC052: "TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256",
0xC053: "TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384",
0xC054: "TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256",
0xC055: "TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384",
0xC056: "TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256",
0xC057: "TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384",
0xC058: "TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256",
0xC059: "TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384",
0xC05A: "TLS_DH_anon_WITH_ARIA_128_GCM_SHA256",
0xC05B: "TLS_DH_anon_WITH_ARIA_256_GCM_SHA384",
0xC05C: "TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256",
0xC05D: "TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384",
0xC05E: "TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256",
0xC05F: "TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384",
0xC060: "TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256",
0xC061: "TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384",
0xC062: "TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256",
0xC063: "TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384",
0xC064: "TLS_PSK_WITH_ARIA_128_CBC_SHA256",
0xC065: "TLS_PSK_WITH_ARIA_256_CBC_SHA384",
0xC066: "TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256",
0xC067: "TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384",
0xC068: "TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256",
0xC069: "TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384",
0xC06A: "TLS_PSK_WITH_ARIA_128_GCM_SHA256",
0xC06B: "TLS_PSK_WITH_ARIA_256_GCM_SHA384",
0xC06C: "TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256",
0xC06D: "TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384",
0xC06E: "TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256",
0xC06F: "TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384",
0xC070: "TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256",
0xC071: "TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384",
0xC072: "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256",
0xC073: "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384",
0xC074: "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256",
0xC075: "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384",
0xC076: "TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
0xC077: "TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384",
0xC078: "TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256",
0xC079: "TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384",
0xC07A: "TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256",
0xC07B: "TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384",
0xC07C: "TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256",
0xC07D: "TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384",
0xC07E: "TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256",
0xC07F: "TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384",
0xC080: "TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256",
0xC081: "TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384",
0xC082: "TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256",
0xC083: "TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384",
0xC084: "TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256",
0xC085: "TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384",
0xC086: "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256",
0xC087: "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384",
0xC088: "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256",
0xC089: "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384",
0xC08A: "TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256",
0xC08B: "TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384",
0xC08C: "TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256",
0xC08D: "TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384",
0xC08E: "TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256",
0xC08F: "TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384",
0xC090: "TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256",
0xC091: "TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384",
0xC092: "TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256",
0xC093: "TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384",
0xC094: "TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256",
0xC095: "TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384",
0xC096: "TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256",
0xC097: "TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384",
0xC098: "TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256",
0xC099: "TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384",
0xC09A: "TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256",
0xC09B: "TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384",
0xC09C: "TLS_RSA_WITH_AES_128_CCM",
0xC09D: "TLS_RSA_WITH_AES_256_CCM",
0xC09E: "TLS_DHE_RSA_WITH_AES_128_CCM",
0xC09F: "TLS_DHE_RSA_WITH_AES_256_CCM",
0xC0A0: "TLS_RSA_WITH_AES_128_CCM_8",
0xC0A1: "TLS_RSA_WITH_AES_256_CCM_8",
0xC0A2: "TLS_DHE_RSA_WITH_AES_128_CCM_8",
0xC0A3: "TLS_DHE_RSA_WITH_AES_256_CCM_8",
0xC0A4: "TLS_PSK_WITH_AES_128_CCM",
0xC0A5: "TLS_PSK_WITH_AES_256_CCM",
0xC0A6: "TLS_DHE_PSK_WITH_AES_128_CCM",
0xC0A7: "TLS_DHE_PSK_WITH_AES_256_CCM",
0xC0A8: "TLS_PSK_WITH_AES_128_CCM_8",
0xC0A9: "TLS_PSK_WITH_AES_256_CCM_8",
0xC0AA: "TLS_PSK_DHE_WITH_AES_128_CCM_8",
0xC0AB: "TLS_PSK_DHE_WITH_AES_256_CCM_8",
0xC0AC: "TLS_ECDHE_ECDSA_WITH_AES_128_CCM",
0xC0AD: "TLS_ECDHE_ECDSA_WITH_AES_256_CCM",
0xC0AE: "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8",
0xC0AF: "TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8",
0xC0B0: "TLS_ECCPWD_WITH_AES_128_GCM_SHA256",
0xC0B1: "TLS_ECCPWD_WITH_AES_256_GCM_SHA384",
0xC0B2: "TLS_ECCPWD_WITH_AES_128_CCM_SHA256",
0xC0B3: "TLS_ECCPWD_WITH_AES_256_CCM_SHA384",
0xC0B4: "TLS_SHA256_SHA256",
0xC0B5: "TLS_SHA384_SHA384",
0xC100: "TLS_GOSTR341112_256_WITH_KUZNYECHIK_CTR_OMAC",
0xC101: "TLS_GOSTR341112_256_WITH_MAGMA_CTR_OMAC",
0xC102: "TLS_GOSTR341112_256_WITH_28147_CNT_IMIT",
0xC103: "TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_L",
0xC104: "TLS_GOSTR341112_256_WITH_MAGMA_MGM_L",
0xC105: "TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_S",
0xC106: "TLS_GOSTR341112_256_WITH_MAGMA_MGM_S",
0xCACA: "GREASE",
0xCCA8: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
0xCCA9: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
0xCCAA: "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
0xCCAB: "TLS_PSK_WITH_CHACHA20_POLY1305_SHA256",
0xCCAC: "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
0xCCAD: "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
0xCCAE: "TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256",
0xD001: "TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256",
0xD002: "TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384",
0xD003: "TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256",
0xD005: "TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256",
0xDADA: "GREASE",
0xEAEA: "GREASE",
0xFAFA: "GREASE",
};
/**
* GREASE values
*/
export const GREASE_VALUES = [
0x0a0a,
0x1a1a,
0x2a2a,
0x3a3a,
0x4a4a,
0x5a5a,
0x6a6a,
0x7a7a,
0x8a8a,
0x9a9a,
0xaaaa,
0xbaba,
0xcaca,
0xdada,
0xeaea,
0xfafa
];
/**
* Parses the supported_versions extension and returns the highest supported version.
* @param {Uint8Array} bytes
* @returns {number}
*/
export function parseHighestSupportedVersion(bytes) {
const s = new Stream(bytes);
// Length
let i = s.readInt(1);
let highestVersion = 0;
while (s.hasMore() && i-- > 0) {
const v = s.readInt(2);
if (GREASE_VALUES.includes(v)) continue;
if (v > highestVersion) highestVersion = v;
}
return highestVersion;
}
/**
* Parses the application_layer_protocol_negotiation extension and returns the first value.
* @param {Uint8Array} bytes
* @returns {number}
*/
export function parseFirstALPNValue(bytes) {
const s = new Stream(bytes);
const alpnExtLen = s.readInt(2);
if (alpnExtLen < 3) return "00";
const strLen = s.readInt(1);
if (strLen < 2) return "00";
return s.readString(strLen);
}

View File

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

View File

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

View File

@@ -0,0 +1,73 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import {toJA4} from "../lib/JA4.mjs";
/**
* JA4 Fingerprint operation
*/
class JA4Fingerprint extends Operation {
/**
* JA4Fingerprint constructor
*/
constructor() {
super();
this.name = "JA4 Fingerprint";
this.module = "Crypto";
this.description = "Generates a JA4 fingerprint to help identify TLS clients based on hashing together values from the Client Hello.<br><br>Input: A hex stream of the TLS or QUIC Client Hello packet application layer.";
this.infoURL = "https://medium.com/foxio/ja4-network-fingerprinting-9376fe9ca637";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
name: "Input format",
type: "option",
value: ["Hex", "Base64", "Raw"]
},
{
name: "Output format",
type: "option",
value: ["JA4", "JA4 Original Rendering", "JA4 Raw", "JA4 Raw Original Rendering", "All"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [inputFormat, outputFormat] = args;
input = Utils.convertToByteArray(input, inputFormat);
const ja4 = toJA4(new Uint8Array(input));
// Output
switch (outputFormat) {
case "JA4":
return ja4.JA4;
case "JA4 Original Rendering":
return ja4.JA4_o;
case "JA4 Raw":
return ja4.JA4_r;
case "JA4 Raw Original Rendering":
return ja4.JA4_ro;
case "All":
default:
return `JA4: ${ja4.JA4}
JA4_o: ${ja4.JA4_o}
JA4_r: ${ja4.JA4_r}
JA4_ro: ${ja4.JA4_ro}`;
}
}
}
export default JA4Fingerprint;

View File

@@ -46,6 +46,8 @@ class App {
this.appLoaded = false;
this.workerLoaded = false;
this.waitersLoaded = false;
this.snackbars = [];
}
@@ -500,22 +502,22 @@ class App {
// Input Character Encoding
// Must be set before the input is loaded
if (this.uriParams.ienc) {
this.manager.input.chrEncChange(parseInt(this.uriParams.ienc, 10));
this.manager.input.chrEncChange(parseInt(this.uriParams.ienc, 10), true);
}
// Output Character Encoding
if (this.uriParams.oenc) {
this.manager.output.chrEncChange(parseInt(this.uriParams.oenc, 10));
this.manager.output.chrEncChange(parseInt(this.uriParams.oenc, 10), true);
}
// Input EOL sequence
if (this.uriParams.ieol) {
this.manager.input.eolChange(this.uriParams.ieol);
this.manager.input.eolChange(this.uriParams.ieol, true);
}
// Output EOL sequence
if (this.uriParams.oeol) {
this.manager.output.eolChange(this.uriParams.oeol);
this.manager.output.eolChange(this.uriParams.oeol, true);
}
// Read in input data from URI params
@@ -708,14 +710,14 @@ class App {
log.info("[" + time.toLocaleString() + "] " + str);
if (silent) return;
this.currentSnackbar = $.snackbar({
this.snackbars.push($.snackbar({
content: str,
timeout: timeout,
htmlAllowed: true,
onClose: () => {
this.currentSnackbar.remove();
this.snackbars.shift().remove();
}
});
}));
}

View File

@@ -69,6 +69,10 @@ select.arg {
min-width: 100px;
}
select.arg.form-control:not([size]):not([multiple]), select.custom-file-control:not([size]):not([multiple]) {
height: 100% !important;
}
textarea.arg {
min-height: 74px;
resize: vertical;
@@ -80,7 +84,7 @@ div.toggle-string {
input.toggle-string {
border-top-right-radius: 0 !important;
height: 42px !important;
height: 100%;
}
.operation [class^='bmd-label'],

View File

@@ -95,3 +95,42 @@ export function escapeControlChars(str, preserveWs=false, lineBreak="\n") {
return n.outerHTML;
});
}
/**
* Convert and EOL sequence to its name
*/
export const eolSeqToCode = {
"\u000a": "LF",
"\u000b": "VT",
"\u000c": "FF",
"\u000d": "CR",
"\u000d\u000a": "CRLF",
"\u0085": "NEL",
"\u2028": "LS",
"\u2029": "PS"
};
/**
* Convert an EOL name to its sequence
*/
export const eolCodeToSeq = {
"LF": "\u000a",
"VT": "\u000b",
"FF": "\u000c",
"CR": "\u000d",
"CRLF": "\u000d\u000a",
"NEL": "\u0085",
"LS": "\u2028",
"PS": "\u2029"
};
export const eolCodeToName = {
"LF": "Line Feed",
"VT": "Vertical Tab",
"FF": "Form Feed",
"CR": "Carriage Return",
"CRLF": "Carriage Return + Line Feed",
"NEL": "Next Line",
"LS": "Line Separator",
"PS": "Paragraph Separator"
};

View File

@@ -6,6 +6,7 @@
import {showPanel} from "@codemirror/view";
import {CHR_ENC_SIMPLE_LOOKUP, CHR_ENC_SIMPLE_REVERSE_LOOKUP} from "../../core/lib/ChrEnc.mjs";
import { eolCodeToName, eolSeqToCode } from "./editorUtils.mjs";
/**
* A Status bar extension for CodeMirror
@@ -23,6 +24,8 @@ class StatusBarPanel {
this.eolHandler = opts.eolHandler;
this.chrEncHandler = opts.chrEncHandler;
this.chrEncGetter = opts.chrEncGetter;
this.getEncodingState = opts.getEncodingState;
this.getEOLState = opts.getEOLState;
this.htmlOutput = opts.htmlOutput;
this.eolVal = null;
@@ -92,22 +95,12 @@ class StatusBarPanel {
// preventDefault is required to stop the URL being modified and popState being triggered
e.preventDefault();
const eolLookup = {
"LF": "\u000a",
"VT": "\u000b",
"FF": "\u000c",
"CR": "\u000d",
"CRLF": "\u000d\u000a",
"NEL": "\u0085",
"LS": "\u2028",
"PS": "\u2029"
};
const eolval = eolLookup[e.target.getAttribute("data-val")];
if (eolval === undefined) return;
const eolCode = e.target.getAttribute("data-val");
if (!eolCode) return;
// Call relevant EOL change handler
this.eolHandler(eolval);
this.eolHandler(e.target.getAttribute("data-val"), true);
hideElement(e.target.closest(".cm-status-bar-select-content"));
}
@@ -124,7 +117,7 @@ class StatusBarPanel {
if (isNaN(chrEncVal)) return;
this.chrEncHandler(chrEncVal);
this.chrEncHandler(chrEncVal, true);
this.updateCharEnc(chrEncVal);
hideElement(e.target.closest(".cm-status-bar-select-content"));
}
@@ -221,25 +214,34 @@ class StatusBarPanel {
* @param {EditorState} state
*/
updateEOL(state) {
if (state.lineBreak === this.eolVal) return;
const eolLookup = {
"\u000a": ["LF", "Line Feed"],
"\u000b": ["VT", "Vertical Tab"],
"\u000c": ["FF", "Form Feed"],
"\u000d": ["CR", "Carriage Return"],
"\u000d\u000a": ["CRLF", "Carriage Return + Line Feed"],
"\u0085": ["NEL", "Next Line"],
"\u2028": ["LS", "Line Separator"],
"\u2029": ["PS", "Paragraph Separator"]
};
if (this.getEOLState() < 2 && state.lineBreak === this.eolVal) return;
const val = this.dom.querySelector(".eol-value");
const button = val.closest(".cm-status-bar-select-btn");
const eolName = eolLookup[state.lineBreak];
val.textContent = eolName[0];
button.setAttribute("title", `End of line sequence:<br>${eolName[1]}`);
button.setAttribute("data-original-title", `End of line sequence:<br>${eolName[1]}`);
let eolCode = eolSeqToCode[state.lineBreak];
let eolName = eolCodeToName[eolCode];
switch (this.getEOLState()) {
case 1: // Detected
val.classList.add("font-italic");
eolCode += " (detected)";
eolName += " (detected)";
// Pulse
val.classList.add("pulse");
setTimeout(() => {
val.classList.remove("pulse");
}, 2000);
break;
case 0: // Unset
case 2: // Manually set
default:
val.classList.remove("font-italic");
break;
}
val.textContent = eolCode;
button.setAttribute("title", `End of line sequence:<br>${eolName}`);
button.setAttribute("data-original-title", `End of line sequence:<br>${eolName}`);
this.eolVal = state.lineBreak;
}
@@ -249,12 +251,30 @@ class StatusBarPanel {
*/
updateCharEnc() {
const chrEncVal = this.chrEncGetter();
if (chrEncVal === this.chrEncVal) return;
if (this.getEncodingState() < 2 && chrEncVal === this.chrEncVal) return;
const name = CHR_ENC_SIMPLE_REVERSE_LOOKUP[chrEncVal] ? CHR_ENC_SIMPLE_REVERSE_LOOKUP[chrEncVal] : "Raw Bytes";
let name = CHR_ENC_SIMPLE_REVERSE_LOOKUP[chrEncVal] ? CHR_ENC_SIMPLE_REVERSE_LOOKUP[chrEncVal] : "Raw Bytes";
const val = this.dom.querySelector(".chr-enc-value");
const button = val.closest(".cm-status-bar-select-btn");
switch (this.getEncodingState()) {
case 1: // Detected
val.classList.add("font-italic");
name += " (detected)";
// Pulse
val.classList.add("pulse");
setTimeout(() => {
val.classList.remove("pulse");
}, 2000);
break;
case 0: // Unset
case 2: // Manually set
default:
val.classList.remove("font-italic");
break;
}
val.textContent = name;
button.setAttribute("title", `${this.label} character encoding:<br>${name}`);
button.setAttribute("data-original-title", `${this.label} character encoding:<br>${name}`);

View File

@@ -5,6 +5,7 @@
*/
import Utils from "../../core/Utils.mjs";
import { eolSeqToCode } from "../utils/editorUtils.mjs";
/**
@@ -140,16 +141,16 @@ class ControlsWaiter {
const inputChrEnc = this.manager.input.getChrEnc();
const outputChrEnc = this.manager.output.getChrEnc();
const inputEOLSeq = this.manager.input.getEOLSeq();
const outputEOLSeq = this.manager.output.getEOLSeq();
const inputEOL = eolSeqToCode[this.manager.input.getEOLSeq()];
const outputEOL = eolSeqToCode[this.manager.output.getEOLSeq()];
const params = [
includeRecipe ? ["recipe", recipeStr] : undefined,
includeInput && input.length ? ["input", Utils.escapeHtml(input)] : undefined,
inputChrEnc !== 0 ? ["ienc", inputChrEnc] : undefined,
outputChrEnc !== 0 ? ["oenc", outputChrEnc] : undefined,
inputEOLSeq !== "\n" ? ["ieol", inputEOLSeq] : undefined,
outputEOLSeq !== "\n" ? ["oeol", outputEOLSeq] : undefined
inputEOL !== "LF" ? ["ieol", inputEOL] : undefined,
outputEOL !== "LF" ? ["oeol", outputEOL] : undefined
];
const hash = params

View File

@@ -42,7 +42,7 @@ import {
import {statusBar} from "../utils/statusBar.mjs";
import {fileDetailsPanel} from "../utils/fileDetails.mjs";
import {renderSpecialChar} from "../utils/editorUtils.mjs";
import {eolCodeToSeq, eolCodeToName, renderSpecialChar} from "../utils/editorUtils.mjs";
/**
@@ -62,6 +62,8 @@ class InputWaiter {
this.inputTextEl = document.getElementById("input-text");
this.inputChrEnc = 0;
this.eolState = 0; // 0 = unset, 1 = detected, 2 = manual
this.encodingState = 0; // 0 = unset, 1 = detected, 2 = manual
this.initEditor();
this.inputWorker = null;
@@ -92,6 +94,7 @@ class InputWaiter {
fileDetailsPanel: new Compartment
};
const self = this;
const initialState = EditorState.create({
doc: null,
extensions: [
@@ -114,7 +117,9 @@ class InputWaiter {
label: "Input",
eolHandler: this.eolChange.bind(this),
chrEncHandler: this.chrEncChange.bind(this),
chrEncGetter: this.getChrEnc.bind(this)
chrEncGetter: this.getChrEnc.bind(this),
getEncodingState: this.getEncodingState.bind(this),
getEOLState: this.getEOLState.bind(this)
}),
// Mutable state
@@ -141,10 +146,21 @@ class InputWaiter {
if (e.docChanged && !this.silentInputChange)
this.inputChange(e);
this.silentInputChange = false;
}),
// Event handlers
EditorView.domEventHandlers({
paste(event, view) {
setTimeout(() => {
self.afterPaste(event);
});
}
})
]
});
if (this.inputEditorView) this.inputEditorView.destroy();
this.inputEditorView = new EditorView({
state: initialState,
parent: this.inputTextEl
@@ -154,12 +170,23 @@ class InputWaiter {
/**
* Handler for EOL change events
* Sets the line separator
* @param {string} eolVal
* @param {string} eol
* @param {boolean} [manual=false]
*/
eolChange(eolVal) {
const oldInputVal = this.getInput();
eolChange(eol, manual=false) {
const eolVal = eolCodeToSeq[eol];
if (eolVal === undefined) return;
this.eolState = manual ? 2 : this.eolState;
if (this.eolState < 2 && eolVal === this.getEOLSeq()) return;
if (this.eolState === 1) {
// Alert
this.app.alert(`Input end of line separator has been detected and changed to ${eolCodeToName[eol]}`, 5000);
}
// Update the EOL value
const oldInputVal = this.getInput();
this.inputEditorView.dispatch({
effects: this.inputEditorConf.eol.reconfigure(EditorState.lineSeparator.of(eolVal))
});
@@ -176,14 +203,24 @@ class InputWaiter {
return this.inputEditorView.state.lineBreak;
}
/**
* Returns whether the input EOL sequence was set manually or has been detected automatically
* @returns {number} - 0 = unset, 1 = detected, 2 = manual
*/
getEOLState() {
return this.eolState;
}
/**
* Handler for Chr Enc change events
* Sets the input character encoding
* @param {number} chrEncVal
* @param {boolean} [manual=false]
*/
chrEncChange(chrEncVal) {
chrEncChange(chrEncVal, manual=false) {
if (typeof chrEncVal !== "number") return;
this.inputChrEnc = chrEncVal;
this.encodingState = manual ? 2 : this.encodingState;
this.inputChange();
}
@@ -195,6 +232,14 @@ class InputWaiter {
return this.inputChrEnc;
}
/**
* Returns whether the input character encoding was set manually or has been detected automatically
* @returns {number} - 0 = unset, 1 = detected, 2 = manual
*/
getEncodingState() {
return this.encodingState;
}
/**
* Sets word wrap on the input editor
* @param {boolean} wrap
@@ -866,6 +911,55 @@ class InputWaiter {
}, delay, "inputChange", this, [e])();
}
/**
* Handler that fires just after input paste events.
* Checks whether the EOL separator or character encoding should be updated.
*
* @param {event} e
*/
afterPaste(e) {
// If EOL has been fixed, skip this.
if (this.eolState > 1) return;
const inputText = this.getInput();
// Detect most likely EOL sequence
const eolCharCounts = {
"LF": inputText.count("\u000a"),
"VT": inputText.count("\u000b"),
"FF": inputText.count("\u000c"),
"CR": inputText.count("\u000d"),
"CRLF": inputText.count("\u000d\u000a"),
"NEL": inputText.count("\u0085"),
"LS": inputText.count("\u2028"),
"PS": inputText.count("\u2029")
};
// If all zero, leave alone
const total = Object.values(eolCharCounts).reduce((acc, curr) => {
return acc + curr;
}, 0);
if (total === 0) return;
// Find most prevalent line ending sequence
const highest = Object.entries(eolCharCounts).reduce((acc, curr) => {
return curr[1] > acc[1] ? curr : acc;
}, ["LF", 0]);
let choice = highest[0];
// If CRLF not zero and more than half the highest alternative, choose CRLF
if ((eolCharCounts.CRLF * 2) > highest[1]) {
choice = "CRLF";
}
const eolVal = eolCodeToSeq[choice];
if (eolVal === this.getEOLSeq()) return;
// Setting automatically
this.eolState = 1;
this.eolChange(choice);
}
/**
* Handler for input dragover events.
* Gives the user a visual cue to show that items can be dropped here.
@@ -1199,6 +1293,14 @@ class InputWaiter {
this.manager.output.removeAllOutputs();
this.manager.output.terminateZipWorker();
this.eolState = 0;
this.encodingState = 0;
this.manager.output.eolState = 0;
this.manager.output.encodingState = 0;
this.initEditor();
this.manager.output.initEditor();
const tabsList = document.getElementById("input-tabs");
const tabsListChildren = tabsList.children;

View File

@@ -7,6 +7,7 @@
import Utils, {debounce} from "../../core/Utils.mjs";
import Dish from "../../core/Dish.mjs";
import {isUTF8, CHR_ENC_SIMPLE_REVERSE_LOOKUP} from "../../core/lib/ChrEnc.mjs";
import {detectFileType} from "../../core/lib/FileType.mjs";
import FileSaver from "file-saver";
import ZipWorker from "worker-loader?inline=no-fallback!../workers/ZipWorker.mjs";
@@ -38,7 +39,7 @@ import {
import {statusBar} from "../utils/statusBar.mjs";
import {htmlPlugin} from "../utils/htmlWidget.mjs";
import {copyOverride} from "../utils/copyOverride.mjs";
import {renderSpecialChar} from "../utils/editorUtils.mjs";
import {eolCodeToSeq, eolCodeToName, renderSpecialChar} from "../utils/editorUtils.mjs";
/**
@@ -70,6 +71,8 @@ class OutputWaiter {
this.zipWorker = null;
this.maxTabs = this.manager.tabs.calcMaxTabs();
this.tabTimeout = null;
this.eolState = 0; // 0 = unset, 1 = detected, 2 = manual
this.encodingState = 0; // 0 = unset, 1 = detected, 2 = manual
}
/**
@@ -109,6 +112,8 @@ class OutputWaiter {
eolHandler: this.eolChange.bind(this),
chrEncHandler: this.chrEncChange.bind(this),
chrEncGetter: this.getChrEnc.bind(this),
getEncodingState: this.getEncodingState.bind(this),
getEOLState: this.getEOLState.bind(this),
htmlOutput: this.htmlOutput
}),
htmlPlugin(this.htmlOutput),
@@ -137,6 +142,7 @@ class OutputWaiter {
]
});
if (this.outputEditorView) this.outputEditorView.destroy();
this.outputEditorView = new EditorView({
state: initialState,
parent: this.outputTextEl
@@ -146,9 +152,21 @@ class OutputWaiter {
/**
* Handler for EOL change events
* Sets the line separator
* @param {string} eolVal
* @param {string} eol
* @param {boolean} [manual=false]
*/
async eolChange(eolVal) {
async eolChange(eol, manual=false) {
const eolVal = eolCodeToSeq[eol];
if (eolVal === undefined) return;
this.eolState = manual ? 2 : this.eolState;
if (this.eolState < 2 && eolVal === this.getEOLSeq()) return;
if (this.eolState === 1) {
// Alert
this.app.alert(`Output end of line separator has been detected and changed to ${eolCodeToName[eol]}`, 5000);
}
const currentTabNum = this.manager.tabs.getActiveTab("output");
if (currentTabNum >= 0) {
this.outputs[currentTabNum].eolSequence = eolVal;
@@ -180,13 +198,23 @@ class OutputWaiter {
return this.outputs[currentTabNum].eolSequence;
}
/**
* Returns whether the output EOL sequence was set manually or has been detected automatically
* @returns {number} - 0 = unset, 1 = detected, 2 = manual
*/
getEOLState() {
return this.eolState;
}
/**
* Handler for Chr Enc change events
* Sets the output character encoding
* @param {number} chrEncVal
* @param {boolean} [manual=false]
*/
async chrEncChange(chrEncVal) {
async chrEncChange(chrEncVal, manual=false) {
if (typeof chrEncVal !== "number") return;
const currentEnc = this.getChrEnc();
const currentTabNum = this.manager.tabs.getActiveTab("output");
if (currentTabNum >= 0) {
@@ -195,10 +223,17 @@ class OutputWaiter {
throw new Error(`Cannot change output ${currentTabNum} chrEnc to ${chrEncVal}`);
}
// Reset the output, forcing it to re-decode the data with the new character encoding
await this.setOutput(this.currentOutputCache, true);
// Update the URL manually since we aren't firing a statechange event
this.app.updateURL(true);
this.encodingState = manual ? 2 : this.encodingState;
if (this.encodingState > 1) {
// Reset the output, forcing it to re-decode the data with the new character encoding
await this.setOutput(this.currentOutputCache, true);
// Update the URL manually since we aren't firing a statechange event
this.app.updateURL(true);
} else if (currentEnc !== chrEncVal) {
// Alert
this.app.alert(`Output character encoding has been detected and changed to ${CHR_ENC_SIMPLE_REVERSE_LOOKUP[chrEncVal] || "Raw Bytes"}`, 5000);
}
}
/**
@@ -213,6 +248,14 @@ class OutputWaiter {
return this.outputs[currentTabNum].encoding;
}
/**
* Returns whether the output character encoding was set manually or has been detected automatically
* @returns {number} - 0 = unset, 1 = detected, 2 = manual
*/
getEncodingState() {
return this.encodingState;
}
/**
* Sets word wrap on the output editor
* @param {boolean} wrap
@@ -248,6 +291,7 @@ class OutputWaiter {
const tabNum = this.manager.tabs.getActiveTab("output");
this.manager.timing.recordTime("outputDecodingStart", tabNum);
if (data instanceof ArrayBuffer) {
await this.detectEncoding(data);
data = await this.bufferToStr(data);
}
this.manager.timing.recordTime("outputDecodingEnd", tabNum);
@@ -276,6 +320,9 @@ class OutputWaiter {
// If turning word wrap off, do it before we populate the editor for performance reasons
if (!wrap) this.setWordWrap(wrap);
// Detect suitable EOL sequence
this.detectEOLSequence(data);
// We use setTimeout here to delay the editor dispatch until the next event cycle,
// ensuring all async actions have completed before attempting to set the contents
// of the editor. This is mainly with the above call to setWordWrap() in mind.
@@ -345,6 +392,85 @@ class OutputWaiter {
});
}
/**
* Checks whether the EOL separator should be updated
*
* @param {string} data
*/
detectEOLSequence(data) {
// If EOL has been fixed, skip this.
if (this.eolState > 1) return;
// If data is too long, skip this.
if (data.length > 1000000) return;
// Detect most likely EOL sequence
const eolCharCounts = {
"LF": data.count("\u000a"),
"VT": data.count("\u000b"),
"FF": data.count("\u000c"),
"CR": data.count("\u000d"),
"CRLF": data.count("\u000d\u000a"),
"NEL": data.count("\u0085"),
"LS": data.count("\u2028"),
"PS": data.count("\u2029")
};
// If all zero, leave alone
const total = Object.values(eolCharCounts).reduce((acc, curr) => {
return acc + curr;
}, 0);
if (total === 0) return;
// Find most prevalent line ending sequence
const highest = Object.entries(eolCharCounts).reduce((acc, curr) => {
return curr[1] > acc[1] ? curr : acc;
}, ["LF", 0]);
let choice = highest[0];
// If CRLF not zero and more than half the highest alternative, choose CRLF
if ((eolCharCounts.CRLF * 2) > highest[1]) {
choice = "CRLF";
}
const eolVal = eolCodeToSeq[choice];
if (eolVal === this.getEOLSeq()) return;
// Setting automatically
this.eolState = 1;
this.eolChange(choice);
}
/**
* Checks whether the character encoding should be updated.
*
* @param {ArrayBuffer} data
*/
async detectEncoding(data) {
// If encoding has been fixed, skip this.
if (this.encodingState > 1) return;
// If data is too long, skip this.
if (data.byteLength > 1000000) return;
const enc = isUTF8(data); // 0 = not UTF8, 1 = ASCII, 2 = UTF8
switch (enc) {
case 0: // not UTF8
// Set to Raw Bytes
this.encodingState = 1;
await this.chrEncChange(0, false);
break;
case 2: // UTF8
// Set to UTF8
this.encodingState = 1;
await this.chrEncChange(65001, false);
break;
case 1: // ASCII
default:
// Ignore
break;
}
}
/**
* Calculates the maximum number of tabs to display
*/

View File

@@ -194,6 +194,9 @@ module.exports = {
// Open category
browser
.useCss()
.waitForElementNotVisible("#snackbar-container", 10000)
.useXpath()
.click(otherCat)
.expect.element(genUUID).to.be.visible;
@@ -230,8 +233,8 @@ module.exports = {
// Alert bar shows and contains correct content
browser
.waitForElementNotVisible("#snackbar-container")
.click("#copy-output")
.waitForElementVisible("#snackbar-container")
.waitForElementVisible("#snackbar-container .snackbar-content")
.expect.element("#snackbar-container .snackbar-content").text.to.equal("Copied raw output successfully.");

View File

@@ -545,8 +545,8 @@ module.exports = {
browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("2");
/* Line endings appear in the URL */
browser.assert.urlContains("ieol=%0D%0A");
browser.assert.urlContains("oeol=%0D");
browser.assert.urlContains("ieol=CRLF");
browser.assert.urlContains("oeol=CR");
/* Preserved when changing tabs */
browser
@@ -643,7 +643,7 @@ module.exports = {
"Loading from URL": browser => {
/* Complex deep link populates the input correctly (encoding, eol, input) */
browser
.urlHash("recipe=To_Base64('A-Za-z0-9%2B/%3D')&input=VGhlIHNoaXBzIGh1bmcgaW4gdGhlIHNreSBpbiBtdWNoIHRoZSBzYW1lIHdheSB0aGF0IGJyaWNrcyBkb24ndC4M&ienc=21866&oenc=1201&ieol=%0C&oeol=%E2%80%A9")
.urlHash("recipe=To_Base64('A-Za-z0-9%2B/%3D')&input=VGhlIHNoaXBzIGh1bmcgaW4gdGhlIHNreSBpbiBtdWNoIHRoZSBzYW1lIHdheSB0aGF0IGJyaWNrcyBkb24ndC4M&ienc=21866&oenc=1201&ieol=FF&oeol=PS")
.waitForElementVisible("#rec-list li.operation");
browser.expect.element(`#input-text .cm-content`).to.have.property("textContent").match(/^.{65}$/);

View File

@@ -65,6 +65,7 @@ function setChrEnc(browser, io, enc) {
io = `#${io}-text`;
browser
.useCss()
.waitForElementNotVisible("#snackbar-container", 6000)
.click(io + " .chr-enc-value")
.waitForElementVisible(io + " .chr-enc-select .cm-status-bar-select-scroll")
.click("link text", enc)
@@ -83,6 +84,7 @@ function setEOLSeq(browser, io, eol) {
io = `#${io}-text`;
browser
.useCss()
.waitForElementNotVisible("#snackbar-container", 6000)
.click(io + " .eol-value")
.waitForElementVisible(io + " .eol-select .cm-status-bar-select-content")
.click(`${io} .cm-status-bar-select-content a[data-val=${eol}]`)

View File

@@ -136,7 +136,7 @@ TestRegister.addApiTests([
it("chef.help: returns multiple results", () => {
const result = chef.help("base 64");
assert.strictEqual(result.length, 11);
assert.strictEqual(result.length, 13);
}),
it("chef.help: looks in description for matches too", () => {

View File

@@ -81,6 +81,7 @@ import "./tests/HKDF.mjs";
import "./tests/Image.mjs";
import "./tests/IndexOfCoincidence.mjs";
import "./tests/JA3Fingerprint.mjs";
import "./tests/JA4Fingerprint.mjs";
import "./tests/JA3SFingerprint.mjs";
import "./tests/JSONBeautify.mjs";
import "./tests/JSONMinify.mjs";

View File

@@ -0,0 +1,80 @@
/**
* Fernet tests.
*
* @author Karsten Silkenbäumer [github.com/kassi]
* @copyright Karsten Silkenbäumer 2019
* @license Apache-2.0
*/
import TestRegister from "../TestRegister";
TestRegister.addTests([
{
name: "Fernet Decrypt: no input",
input: "",
expectedOutput: "Error: Invalid version",
recipeConfig: [
{
op: "Fernet Decrypt",
args: ["MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="]
}
],
},
{
name: "Fernet Decrypt: no secret",
input: "gAAAAABce-Tycae8klRxhDX2uenJ-uwV8-A1XZ2HRnfOXlNzkKKfRxviNLlgtemhT_fd1Fw5P_zFUAjd69zaJBQyWppAxVV00SExe77ql8c5n62HYJOnoIU=",
expectedOutput: "Error: Secret must be 32 url-safe base64-encoded bytes.",
recipeConfig: [
{
op: "Fernet Decrypt",
args: [""]
}
],
},
{
name: "Fernet Decrypt: valid arguments",
input: "gAAAAABce-Tycae8klRxhDX2uenJ-uwV8-A1XZ2HRnfOXlNzkKKfRxviNLlgtemhT_fd1Fw5P_zFUAjd69zaJBQyWppAxVV00SExe77ql8c5n62HYJOnoIU=",
expectedOutput: "This is a secret message.\n",
recipeConfig: [
{
op: "Fernet Decrypt",
args: ["VGhpc0lzVGhpcnR5VHdvQ2hhcmFjdGVyc0xvbmdLZXk="]
}
],
}
]);
TestRegister.addTests([
{
name: "Fernet Encrypt: no input",
input: "",
expectedMatch: /^gAAAAABce-[\w-]+={0,2}$/,
recipeConfig: [
{
op: "Fernet Encrypt",
args: ["MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="]
}
],
},
{
name: "Fernet Encrypt: no secret",
input: "This is a secret message.\n",
expectedOutput: "Error: Secret must be 32 url-safe base64-encoded bytes.",
recipeConfig: [
{
op: "Fernet Encrypt",
args: [""]
}
],
},
{
name: "Fernet Encrypt: valid arguments",
input: "This is a secret message.\n",
expectedMatch: /^gAAAAABce-[\w-]+={0,2}$/,
recipeConfig: [
{
op: "Fernet Encrypt",
args: ["MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="]
}
],
}
]);

View File

@@ -0,0 +1,55 @@
/**
* JA4Fingerprint tests.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2024
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "JA4 Fingerprint: TLS 1.3",
input: "1603010200010001fc0303b2c03e7ba990ef540c316a665d4d925f8e9079ac4b15687e587dc99016e75a6c20d0b0099243c9296a0c84153ea4ada7d87ad017f4211c2ea1350b0b3cc5514d5f00205a5a130113021303c02bc02fc02cc030cca9cca8c013c014009c009d002f003501000193fafa000000000024002200001f636f6e74656e742d6175746f66696c6c2e676f6f676c65617069732e636f6d0033002b00293a3a000100001d0020fb2cd8ef3d605b96ab03119ec4f30a6e2088cb1af86c41a81feace8706068c50000d001200100403080404010503080505010806060100230000000b00020100ff01000100000a000a00083a3a001d00170018001b000302000244690005000302683200120000002d000201010010000e000c02683208687474702f312e31000500050100000000002b0007060a0a03040303001700001a1a000100001500b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
expectedOutput: "t13d1516h2_8daaf6152771_e5627efa2ab1",
recipeConfig: [
{
"op": "JA4 Fingerprint",
"args": ["Hex", "JA4"]
}
],
},
{
name: "JA4 Fingerprint: TLS 1.3 Original Rendering",
input: "1603010200010001fc0303b2c03e7ba990ef540c316a665d4d925f8e9079ac4b15687e587dc99016e75a6c20d0b0099243c9296a0c84153ea4ada7d87ad017f4211c2ea1350b0b3cc5514d5f00205a5a130113021303c02bc02fc02cc030cca9cca8c013c014009c009d002f003501000193fafa000000000024002200001f636f6e74656e742d6175746f66696c6c2e676f6f676c65617069732e636f6d0033002b00293a3a000100001d0020fb2cd8ef3d605b96ab03119ec4f30a6e2088cb1af86c41a81feace8706068c50000d001200100403080404010503080505010806060100230000000b00020100ff01000100000a000a00083a3a001d00170018001b000302000244690005000302683200120000002d000201010010000e000c02683208687474702f312e31000500050100000000002b0007060a0a03040303001700001a1a000100001500b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
expectedOutput: "t13d1516h2_acb858a92679_5276cb03a33b",
recipeConfig: [
{
"op": "JA4 Fingerprint",
"args": ["Hex", "JA4 Original Rendering"]
}
],
},
{
name: "JA4 Fingerprint: TLS 1.2",
input: "1603010200010001fc0303ecb2691addb2bf6c599c7aaae23de5f42561cc04eb41029acc6fc050a16ac1d22046f8617b580ac9358e2aa44e306d52466bcc989c87c8ca64309f5faf50ba7b4d0022130113031302c02bc02fcca9cca8c02cc030c00ac009c013c014009c009d002f00350100019100000021001f00001c636f6e74696c652e73657276696365732e6d6f7a696c6c612e636f6d00170000ff01000100000a000e000c001d00170018001901000101000b00020100002300000010000e000c02683208687474702f312e310005000501000000000022000a000804030503060302030033006b0069001d00208909858fbeb6ed2f1248ba5b9e2978bead0e840110192c61daed0096798b184400170041044d183d91f5eed35791fa982464e3b0214aaa5f5d1b78616d9b9fbebc22d11f535b2f94c686143136aa795e6e5a875d6c08064ad5b76d44caad766e2483012748002b00050403040303000d0018001604030503060308040805080604010501060102030201002d00020101001c000240010015007a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
expectedOutput: "t13d1715h2_5b57614c22b0_3d5424432f57",
recipeConfig: [
{
"op": "JA4 Fingerprint",
"args": ["Hex", "JA4"]
}
],
},
{
name: "JA4 Fingerprint: TLS 1.2 Original Rendering",
input: "1603010200010001fc0303ecb2691addb2bf6c599c7aaae23de5f42561cc04eb41029acc6fc050a16ac1d22046f8617b580ac9358e2aa44e306d52466bcc989c87c8ca64309f5faf50ba7b4d0022130113031302c02bc02fcca9cca8c02cc030c00ac009c013c014009c009d002f00350100019100000021001f00001c636f6e74696c652e73657276696365732e6d6f7a696c6c612e636f6d00170000ff01000100000a000e000c001d00170018001901000101000b00020100002300000010000e000c02683208687474702f312e310005000501000000000022000a000804030503060302030033006b0069001d00208909858fbeb6ed2f1248ba5b9e2978bead0e840110192c61daed0096798b184400170041044d183d91f5eed35791fa982464e3b0214aaa5f5d1b78616d9b9fbebc22d11f535b2f94c686143136aa795e6e5a875d6c08064ad5b76d44caad766e2483012748002b00050403040303000d0018001604030503060308040805080604010501060102030201002d00020101001c000240010015007a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
expectedOutput: "t13d1715h2_5b234860e130_014157ec0da2",
recipeConfig: [
{
"op": "JA4 Fingerprint",
"args": ["Hex", "JA4 Original Rendering"]
}
],
},
]);

View File

@@ -1,6 +1,6 @@
/**
* RAKE, Rapid Automatic Keyword Extraction tests.
*
*
* @author sw5678
* @copyright Crown Copyright 2024
* @license Apache-2.0