Skip to content

Commit 289a417

Browse files
committed
Added 'JA3S Fingerprint' operation
1 parent 8379a9b commit 289a417

File tree

6 files changed

+217
-16
lines changed

6 files changed

+217
-16
lines changed

src/core/config/Categories.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,8 @@
193193
"Protobuf Decode",
194194
"VarInt Encode",
195195
"VarInt Decode",
196-
"TLS JA3 Fingerprint",
196+
"JA3 Fingerprint",
197+
"JA3S Fingerprint",
197198
"Format MAC addresses",
198199
"Change IP format",
199200
"Group IP addresses",

src/core/operations/TLSJA3Fingerprint.mjs renamed to src/core/operations/JA3Fingerprint.mjs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,17 @@ import Stream from "../lib/Stream.mjs";
1818
import {runHash} from "../lib/Hash.mjs";
1919

2020
/**
21-
* TLS JA3 Fingerprint operation
21+
* JA3 Fingerprint operation
2222
*/
23-
class TLSJA3Fingerprint extends Operation {
23+
class JA3Fingerprint extends Operation {
2424

2525
/**
26-
* TLSJA3Fingerprint constructor
26+
* JA3Fingerprint constructor
2727
*/
2828
constructor() {
2929
super();
3030

31-
this.name = "TLS JA3 Fingerprint";
31+
this.name = "JA3 Fingerprint";
3232
this.module = "Crypto";
3333
this.description = "Generates a JA3 fingerprint to help identify TLS clients based on hashing together values from the Client Hello.<br><br>Input: A hex stream of the TLS Client Hello application layer.";
3434
this.infoURL = "https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967";
@@ -202,4 +202,4 @@ const GREASE_CIPHERSUITES = [
202202
0xfafa
203203
];
204204

205-
export default TLSJA3Fingerprint;
205+
export default JA3Fingerprint;
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/**
2+
* @author n1474335 [n1474335@gmail.com]
3+
* @copyright Crown Copyright 2021
4+
* @license Apache-2.0
5+
*
6+
* JA3S created by Salesforce
7+
* John B. Althouse
8+
* Jeff Atkinson
9+
* Josh Atkins
10+
*
11+
* Algorithm released under the BSD-3-clause licence
12+
*/
13+
14+
import Operation from "../Operation.mjs";
15+
import OperationError from "../errors/OperationError.mjs";
16+
import Utils from "../Utils.mjs";
17+
import Stream from "../lib/Stream.mjs";
18+
import {runHash} from "../lib/Hash.mjs";
19+
20+
/**
21+
* JA3S Fingerprint operation
22+
*/
23+
class JA3SFingerprint extends Operation {
24+
25+
/**
26+
* JA3SFingerprint constructor
27+
*/
28+
constructor() {
29+
super();
30+
31+
this.name = "JA3S Fingerprint";
32+
this.module = "Crypto";
33+
this.description = "Generates a JA3S fingerprint to help identify TLS servers based on hashing together values from the Server Hello.<br><br>Input: A hex stream of the TLS Server Hello record in the application layer.";
34+
this.infoURL = "https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967";
35+
this.inputType = "string";
36+
this.outputType = "string";
37+
this.args = [
38+
{
39+
name: "Input format",
40+
type: "option",
41+
value: ["Hex", "Base64", "Raw"]
42+
},
43+
{
44+
name: "Output format",
45+
type: "option",
46+
value: ["Hash digest", "JA3S string", "Full details"]
47+
}
48+
];
49+
}
50+
51+
/**
52+
* @param {string} input
53+
* @param {Object[]} args
54+
* @returns {string}
55+
*/
56+
run(input, args) {
57+
const [inputFormat, outputFormat] = args;
58+
59+
input = Utils.convertToByteArray(input, inputFormat);
60+
const s = new Stream(new Uint8Array(input));
61+
62+
const handshake = s.readInt(1);
63+
if (handshake !== 0x16)
64+
throw new OperationError("Not handshake data.");
65+
66+
// Version
67+
s.moveForwardsBy(2);
68+
69+
// Length
70+
const length = s.readInt(2);
71+
if (s.length !== length + 5)
72+
throw new OperationError("Incorrect handshake length.");
73+
74+
// Handshake type
75+
const handshakeType = s.readInt(1);
76+
if (handshakeType !== 2)
77+
throw new OperationError("Not a Server Hello.");
78+
79+
// Handshake length
80+
const handshakeLength = s.readInt(3);
81+
if (s.length !== handshakeLength + 9)
82+
throw new OperationError("Not enough data in Server Hello.");
83+
84+
// Hello version
85+
const helloVersion = s.readInt(2);
86+
87+
// Random
88+
s.moveForwardsBy(32);
89+
90+
// Session ID
91+
const sessionIDLength = s.readInt(1);
92+
s.moveForwardsBy(sessionIDLength);
93+
94+
// Cipher suite
95+
const cipherSuite = s.readInt(2);
96+
97+
// Compression Method
98+
s.moveForwardsBy(1);
99+
100+
// Extensions
101+
const extensionsLength = s.readInt(2);
102+
const extensions = s.getBytes(extensionsLength);
103+
const es = new Stream(extensions);
104+
const exts = [];
105+
while (es.hasMore()) {
106+
const type = es.readInt(2);
107+
const length = es.readInt(2);
108+
es.moveForwardsBy(length);
109+
exts.push(type);
110+
}
111+
112+
// Output
113+
const ja3s = [
114+
helloVersion.toString(),
115+
cipherSuite,
116+
exts.join("-")
117+
];
118+
const ja3sStr = ja3s.join(",");
119+
const ja3sHash = runHash("md5", Utils.strToArrayBuffer(ja3sStr));
120+
121+
switch (outputFormat) {
122+
case "JA3S string":
123+
return ja3sStr;
124+
case "Full details":
125+
return `Hash digest:
126+
${ja3sHash}
127+
128+
Full JA3S string:
129+
${ja3sStr}
130+
131+
TLS Version:
132+
${helloVersion.toString()}
133+
Cipher Suite:
134+
${cipherSuite}
135+
Extensions:
136+
${exts.join("-")}`;
137+
case "Hash digest":
138+
default:
139+
return ja3sHash;
140+
}
141+
}
142+
143+
}
144+
145+
export default JA3SFingerprint;

tests/operations/index.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ import "./tests/Unicode.mjs";
104104
import "./tests/RSA.mjs";
105105
import "./tests/CBOREncode.mjs";
106106
import "./tests/CBORDecode.mjs";
107-
import "./tests/TLSJA3Fingerprint.mjs";
107+
import "./tests/JA3Fingerprint.mjs";
108108

109109

110110
// Cannot test operations that use the File type yet

tests/operations/tests/TLSJA3Fingerprint.mjs renamed to tests/operations/tests/JA3Fingerprint.mjs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* TLSJA3Fingerprint tests.
2+
* JA3Fingerprint tests.
33
*
44
* @author n1474335 [n1474335@gmail.com]
55
* @copyright Crown Copyright 2021
@@ -9,45 +9,45 @@ import TestRegister from "../../lib/TestRegister.mjs";
99

1010
TestRegister.addTests([
1111
{
12-
name: "TLS JA3 Fingerprint: TLS 1.0",
12+
name: "JA3 Fingerprint: TLS 1.0",
1313
input: "16030100a4010000a00301543dd2dd48f517ca9a93b1e599f019fdece704a23e86c1dcac588427abbaddf200005cc014c00a0039003800880087c00fc00500350084c012c00800160013c00dc003000ac013c00900330032009a009900450044c00ec004002f009600410007c011c007c00cc002000500040015001200090014001100080006000300ff0100001b000b000403000102000a000600040018001700230000000f000101",
1414
expectedOutput: "503053a0c5b2bd9b9334bf7f3d3b8852",
1515
recipeConfig: [
1616
{
17-
"op": "TLS JA3 Fingerprint",
17+
"op": "JA3 Fingerprint",
1818
"args": ["Hex", "Hash digest"]
1919
}
2020
],
2121
},
2222
{
23-
name: "TLS JA3 Fingerprint: TLS 1.1",
23+
name: "JA3 Fingerprint: TLS 1.1",
2424
input: "16030100a4010000a00302543dd2ed907e47d0086f34bee2c52dd6ccd8de63ba9387f5e810b09d9d49b38000005cc014c00a0039003800880087c00fc00500350084c012c00800160013c00dc003000ac013c00900330032009a009900450044c00ec004002f009600410007c011c007c00cc002000500040015001200090014001100080006000300ff0100001b000b000403000102000a000600040018001700230000000f000101",
2525
expectedOutput: "a314eb64cee6cb832aaaa372c8295bab",
2626
recipeConfig: [
2727
{
28-
"op": "TLS JA3 Fingerprint",
28+
"op": "JA3 Fingerprint",
2929
"args": ["Hex", "Hash digest"]
3030
}
3131
],
3232
},
3333
{
34-
name: "TLS JA3 Fingerprint: TLS 1.2",
34+
name: "JA3 Fingerprint: TLS 1.2",
3535
input: "1603010102010000fe0303543dd3283283692d85f9416b5ccc65d2aafca45c6530b3c6eafbf6d371b6a015000094c030c02cc028c024c014c00a00a3009f006b006a0039003800880087c032c02ec02ac026c00fc005009d003d00350084c012c00800160013c00dc003000ac02fc02bc027c023c013c00900a2009e0067004000330032009a009900450044c031c02dc029c025c00ec004009c003c002f009600410007c011c007c00cc002000500040015001200090014001100080006000300ff01000041000b000403000102000a000600040018001700230000000d002200200601060206030501050205030401040204030301030203030201020202030101000f000101",
3636
expectedOutput: "c1a36e1a870786cc75edddc0009eaf3a",
3737
recipeConfig: [
3838
{
39-
"op": "TLS JA3 Fingerprint",
39+
"op": "JA3 Fingerprint",
4040
"args": ["Hex", "Hash digest"]
4141
}
4242
],
4343
},
4444
{
45-
name: "TLS JA3 Fingerprint: TLS 1.3",
45+
name: "JA3 Fingerprint: TLS 1.3",
4646
input: "1603010200010001fc03034355d402c132771a9386b6e9994ae37069e0621af504c26673b1343843c21d8d0000264a4a130113021303c02bc02fc02cc030cca9cca8cc14cc13c013c014009c009d002f0035000a010001addada0000ff01000100000000180016000013626c6f672e636c6f7564666c6172652e636f6d0017000000230000000d00140012040308040401050308050501080606010201000500050100000000001200000010000e000c02683208687474702f312e3175500000000b000201000028002b00295a5a000100001d0020cf78b9167af054b922a96752b43973107b2a57766357dd288b2b42ab5df30e08002d00020101002b000b0acaca7f12030303020301000a000a00085a5a001d001700180a0a000100001500e4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
4747
expectedOutput: "4826a90ec2daf4f7b4b64cc1c8bd343b",
4848
recipeConfig: [
4949
{
50-
"op": "TLS JA3 Fingerprint",
50+
"op": "JA3 Fingerprint",
5151
"args": ["Hex", "Hash digest"]
5252
}
5353
],
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* JA3SFingerprint tests.
3+
*
4+
* @author n1474335 [n1474335@gmail.com]
5+
* @copyright Crown Copyright 2021
6+
* @license Apache-2.0
7+
*/
8+
import TestRegister from "../../lib/TestRegister.mjs";
9+
10+
TestRegister.addTests([
11+
{
12+
name: "JA3S Fingerprint: TLS 1.0",
13+
input: "160301003d020000390301543dd2ddedbfe33895bd6bc676a3fa6b9fe5773a6e04d5476d1af3bcbc1dcbbb00c011000011ff01000100000b00040300010200230000",
14+
expectedOutput: "bed95e1b525d2f41db3a6d68fac5b566",
15+
recipeConfig: [
16+
{
17+
"op": "JA3S Fingerprint",
18+
"args": ["Hex", "Hash digest"]
19+
}
20+
],
21+
},
22+
{
23+
name: "JA3S Fingerprint: TLS 1.1",
24+
input: "160302003d020000390302543dd2ed88131999a0120d36c14a4139671d75aae3d7d7779081d3cf7dd7725a00c013000011ff01000100000b00040300010200230000",
25+
expectedOutput: "130fac2dc19b142500acb0abc63b6379",
26+
recipeConfig: [
27+
{
28+
"op": "JA3S Fingerprint",
29+
"args": ["Hex", "Hash digest"]
30+
}
31+
],
32+
},
33+
{
34+
name: "JA3S Fingerprint: TLS 1.2",
35+
input: "160303003d020000390303543dd328b38b445686739d58fab733fa23838f575e0e5ad9a1b9baace6cc3b4100c02f000011ff01000100000b00040300010200230000",
36+
expectedOutput: "ccc514751b175866924439bdbb5bba34",
37+
recipeConfig: [
38+
{
39+
"op": "JA3S Fingerprint",
40+
"args": ["Hex", "Hash digest"]
41+
}
42+
],
43+
},
44+
{
45+
name: "JA3S Fingerprint: TLS 1.3",
46+
input: "16030100520200004e7f123ef1609fd3f4fa8668aac5822d500fb0639b22671d0fb7258597355795511bf61301002800280024001d0020ae0e282a3b7a463e71064ecbaf671586e979b0edbebf7a4735c31678c70f660c",
47+
expectedOutput: "986ae432c402479fe7a0c6fbe02164c1",
48+
recipeConfig: [
49+
{
50+
"op": "JA3S Fingerprint",
51+
"args": ["Hex", "Hash digest"]
52+
}
53+
],
54+
},
55+
]);

0 commit comments

Comments
 (0)