Implement FingerprintClientHello to generate ClientHelloSpec from ClientHello raw bytes (#67)

This commit is contained in:
maxb 2020-12-09 21:37:06 -08:00 committed by GitHub
parent f7e7360167
commit 2179f28668
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 1652 additions and 159 deletions

View file

@ -102,6 +102,26 @@ you can set UConn.HandshakeStateBuilt = true, and marshal clientHello into UConn
In this case you will be responsible for modifying other parts of Config and ClientHelloMsg to reflect your setup In this case you will be responsible for modifying other parts of Config and ClientHelloMsg to reflect your setup
and not confuse "crypto/tls", which will be processing response from server. and not confuse "crypto/tls", which will be processing response from server.
### Fingerprinting Captured Client Hello
You can use a captured client hello to generate new ones that mimic/have the same properties as the original.
The generated client hellos _should_ look like they were generated from the same client software as the original fingerprinted bytes.
In order to do this:
1) Create a `ClientHelloSpec` from the raw bytes of the original client hello
2) Use `HelloCustom` as an argument for `UClient()` to get empty config
3) Use `ApplyPreset` with the generated `ClientHelloSpec` to set the appropriate connection properties
```
uConn := UClient(&net.TCPConn{}, nil, HelloCustom)
fingerprinter := &Fingerprinter{}
generatedSpec, err := fingerprinter.FingerprintClientHello(rawCapturedClientHelloBytes)
if err != nil {
panic("fingerprinting failed: %v", err)
}
if err := uConn.ApplyPreset(generatedSpec); err != nil {
panic("applying generated spec failed: %v", err)
}
```
The `rawCapturedClientHelloBytes` should be the full tls record, including the record type/version/length header.
## Roller ## Roller
A simple wrapper, that allows to easily use multiple latest(auto-updated) fingerprints. A simple wrapper, that allows to easily use multiple latest(auto-updated) fingerprints.

View file

@ -0,0 +1,91 @@
>>> Flow 1 (client to server)
00000000 16 03 01 00 dd 01 00 00 d9 03 03 00 00 00 00 00 |................|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 20 00 00 00 00 |........... ....|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 1c 0a 0a |................|
00000050 c0 2b c0 2f c0 2c c0 30 cc a9 cc a8 c0 13 c0 14 |.+./.,.0........|
00000060 00 9c 00 9d 00 2f 00 35 00 0a 01 00 00 74 0a 0a |...../.5.....t..|
00000070 00 00 ff 01 00 01 00 00 00 00 0b 00 09 00 00 06 |................|
00000080 66 6f 6f 62 61 72 00 17 00 00 00 23 00 00 00 0d |foobar.....#....|
00000090 00 14 00 12 04 03 08 04 04 01 05 03 08 05 05 01 |................|
000000a0 08 06 06 01 02 01 00 05 00 05 01 00 00 00 00 00 |................|
000000b0 12 00 00 00 10 00 0e 00 0c 02 68 32 08 68 74 74 |..........h2.htt|
000000c0 70 2f 31 2e 31 75 50 00 00 00 0b 00 02 01 00 00 |p/1.1uP.........|
000000d0 0a 00 0a 00 08 0a 0a 00 1d 00 17 00 18 1a 1a 00 |................|
000000e0 01 00 |..|
>>> Flow 2 (server to client)
00000000 16 03 03 00 5d 02 00 00 59 03 03 e3 95 26 49 88 |....]...Y....&I.|
00000010 52 b1 c0 bf a0 61 e1 d2 52 f3 cd 1f bb c9 92 66 |R....a..R......f|
00000020 1a 84 ae 97 61 fd 74 68 cc 02 37 20 55 af 37 bb |....a.th..7 U.7.|
00000030 58 09 95 db 22 49 cd b2 c7 ca 5c a5 be b5 b0 b1 |X..."I....\.....|
00000040 cb af ba 27 97 41 d9 30 02 22 da 5d c0 2f 00 00 |...'.A.0.".]./..|
00000050 11 ff 01 00 01 00 00 0b 00 04 03 00 01 02 00 17 |................|
00000060 00 00 16 03 03 02 59 0b 00 02 55 00 02 52 00 02 |......Y...U..R..|
00000070 4f 30 82 02 4b 30 82 01 b4 a0 03 02 01 02 02 09 |O0..K0..........|
00000080 00 e8 f0 9d 3f e2 5b ea a6 30 0d 06 09 2a 86 48 |....?.[..0...*.H|
00000090 86 f7 0d 01 01 0b 05 00 30 1f 31 0b 30 09 06 03 |........0.1.0...|
000000a0 55 04 0a 13 02 47 6f 31 10 30 0e 06 03 55 04 03 |U....Go1.0...U..|
000000b0 13 07 47 6f 20 52 6f 6f 74 30 1e 17 0d 31 36 30 |..Go Root0...160|
000000c0 31 30 31 30 30 30 30 30 30 5a 17 0d 32 35 30 31 |101000000Z..2501|
000000d0 30 31 30 30 30 30 30 30 5a 30 1a 31 0b 30 09 06 |01000000Z0.1.0..|
000000e0 03 55 04 0a 13 02 47 6f 31 0b 30 09 06 03 55 04 |.U....Go1.0...U.|
000000f0 03 13 02 47 6f 30 81 9f 30 0d 06 09 2a 86 48 86 |...Go0..0...*.H.|
00000100 f7 0d 01 01 01 05 00 03 81 8d 00 30 81 89 02 81 |...........0....|
00000110 81 00 db 46 7d 93 2e 12 27 06 48 bc 06 28 21 ab |...F}...'.H..(!.|
00000120 7e c4 b6 a2 5d fe 1e 52 45 88 7a 36 47 a5 08 0d |~...]..RE.z6G...|
00000130 92 42 5b c2 81 c0 be 97 79 98 40 fb 4f 6d 14 fd |.B[.....y.@.Om..|
00000140 2b 13 8b c2 a5 2e 67 d8 d4 09 9e d6 22 38 b7 4a |+.....g....."8.J|
00000150 0b 74 73 2b c2 34 f1 d1 93 e5 96 d9 74 7b f3 58 |.ts+.4......t{.X|
00000160 9f 6c 61 3c c0 b0 41 d4 d9 2b 2b 24 23 77 5b 1c |.la<..A..++$#w[.|
00000170 3b bd 75 5d ce 20 54 cf a1 63 87 1d 1e 24 c4 f3 |;.u]. T..c...$..|
00000180 1d 1a 50 8b aa b6 14 43 ed 97 a7 75 62 f4 14 c8 |..P....C...ub...|
00000190 52 d7 02 03 01 00 01 a3 81 93 30 81 90 30 0e 06 |R.........0..0..|
000001a0 03 55 1d 0f 01 01 ff 04 04 03 02 05 a0 30 1d 06 |.U...........0..|
000001b0 03 55 1d 25 04 16 30 14 06 08 2b 06 01 05 05 07 |.U.%..0...+.....|
000001c0 03 01 06 08 2b 06 01 05 05 07 03 02 30 0c 06 03 |....+.......0...|
000001d0 55 1d 13 01 01 ff 04 02 30 00 30 19 06 03 55 1d |U.......0.0...U.|
000001e0 0e 04 12 04 10 9f 91 16 1f 43 43 3e 49 a6 de 6d |.........CC>I..m|
000001f0 b6 80 d7 9f 60 30 1b 06 03 55 1d 23 04 14 30 12 |....`0...U.#..0.|
00000200 80 10 48 13 49 4d 13 7e 16 31 bb a3 01 d5 ac ab |..H.IM.~.1......|
00000210 6e 7b 30 19 06 03 55 1d 11 04 12 30 10 82 0e 65 |n{0...U....0...e|
00000220 78 61 6d 70 6c 65 2e 67 6f 6c 61 6e 67 30 0d 06 |xample.golang0..|
00000230 09 2a 86 48 86 f7 0d 01 01 0b 05 00 03 81 81 00 |.*.H............|
00000240 9d 30 cc 40 2b 5b 50 a0 61 cb ba e5 53 58 e1 ed |.0.@+[P.a...SX..|
00000250 83 28 a9 58 1a a9 38 a4 95 a1 ac 31 5a 1a 84 66 |.(.X..8....1Z..f|
00000260 3d 43 d3 2d d9 0b f2 97 df d3 20 64 38 92 24 3a |=C.-...... d8.$:|
00000270 00 bc cf 9c 7d b7 40 20 01 5f aa d3 16 61 09 a2 |....}.@ ._...a..|
00000280 76 fd 13 c3 cc e1 0c 5c ee b1 87 82 f1 6c 04 ed |v......\.....l..|
00000290 73 bb b3 43 77 8d 0c 1c f1 0f a1 d8 40 83 61 c9 |s..Cw.......@.a.|
000002a0 4c 72 2b 9d ae db 46 06 06 4d f4 c1 b3 3e c0 d1 |Lr+...F..M...>..|
000002b0 bd 42 d4 db fe 3d 13 60 84 5c 21 d3 3b e9 fa e7 |.B...=.`.\!.;...|
000002c0 16 03 03 00 ac 0c 00 00 a8 03 00 1d 20 c0 48 97 |............ .H.|
000002d0 bf 0d 8d 9e f9 bb 77 f4 0f 86 cc 81 ad 94 74 0e |......w.......t.|
000002e0 9d ef b2 27 52 f2 cf 60 1c 84 4b c3 59 08 04 00 |...'R..`..K.Y...|
000002f0 80 02 45 0b 36 e5 4b 48 d8 c3 7e 4f c3 43 6b b4 |..E.6.KH..~O.Ck.|
00000300 bd 6a a2 04 ee c4 59 32 a3 d2 5d 72 8d d8 49 b9 |.j....Y2..]r..I.|
00000310 de 31 04 bf dc f3 1a d8 31 d4 eb b4 98 e4 2c d3 |.1......1.....,.|
00000320 50 42 f6 cc 41 2f 75 aa 63 19 99 49 38 ec 0b ed |PB..A/u.c..I8...|
00000330 59 6a 82 58 ea 9c 58 d8 6b 88 60 35 b7 06 21 10 |Yj.X..X.k.`5..!.|
00000340 54 c8 d4 6b af fc 6e 2b 90 15 b1 87 ed aa 96 bb |T..k..n+........|
00000350 ad 8d dc e1 7b 48 bf da d2 70 eb cf 73 4a e6 60 |....{H...p..sJ.`|
00000360 ac ef 39 57 a6 ff 5d 34 bb 7b e1 e0 e5 11 67 62 |..9W..]4.{....gb|
00000370 6c 16 03 03 00 04 0e 00 00 00 |l.........|
>>> Flow 3 (client to server)
00000000 16 03 03 00 25 10 00 00 21 20 2f e5 7d a3 47 cd |....%...! /.}.G.|
00000010 62 43 15 28 da ac 5f bb 29 07 30 ff f6 84 af c4 |bC.(.._.).0.....|
00000020 cf c2 ed 90 99 5f 58 cb 3b 74 14 03 03 00 01 01 |....._X.;t......|
00000030 16 03 03 00 28 00 00 00 00 00 00 00 00 eb f8 36 |....(..........6|
00000040 16 8e 9a 9f 52 62 a6 85 c3 05 4b 76 16 35 63 fe |....Rb....Kv.5c.|
00000050 0c c3 4a 1e f0 0e 24 b5 6e db 0d bc 65 |..J...$.n...e|
>>> Flow 4 (server to client)
00000000 14 03 03 00 01 01 16 03 03 00 28 5f 6d 17 67 11 |..........(_m.g.|
00000010 3a 9b 08 87 88 c2 6d ee 8a 1a 0f e9 30 fe 9a 30 |:.....m.....0..0|
00000020 63 ad 2e a1 a6 8f b8 4e fa c9 65 64 87 96 b4 66 |c......N..ed...f|
00000030 39 03 7b |9.{|
>>> Flow 5 (client to server)
00000000 17 03 03 00 1e 00 00 00 00 00 00 00 01 68 fb 78 |.............h.x|
00000010 2f 1b 5b 21 fc e3 f5 39 69 29 63 35 54 71 06 4a |/.[!...9i)c5Tq.J|
00000020 fc f1 f9 15 03 03 00 1a 00 00 00 00 00 00 00 02 |................|
00000030 10 fb 34 b1 37 06 12 4b c2 17 dd ef 2f 52 2e 22 |..4.7..K..../R."|
00000040 60 4f |`O|

View file

@ -0,0 +1,107 @@
>>> Flow 1 (client to server)
00000000 16 03 01 02 00 01 00 01 fc 03 03 00 00 00 00 00 |................|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 20 00 00 00 00 |........... ....|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 22 0a 0a |............."..|
00000050 13 01 13 02 13 03 c0 2b c0 2f c0 2c c0 30 cc a9 |.......+./.,.0..|
00000060 cc a8 c0 13 c0 14 00 9c 00 9d 00 2f 00 35 00 0a |.........../.5..|
00000070 01 00 01 91 0a 0a 00 00 ff 01 00 01 00 00 00 00 |................|
00000080 0b 00 09 00 00 06 66 6f 6f 62 61 72 00 17 00 00 |......foobar....|
00000090 00 23 00 00 00 0d 00 14 00 12 04 03 08 04 04 01 |.#..............|
000000a0 05 03 08 05 05 01 08 06 06 01 02 01 00 05 00 05 |................|
000000b0 01 00 00 00 00 00 12 00 00 00 10 00 0e 00 0c 02 |................|
000000c0 68 32 08 68 74 74 70 2f 31 2e 31 75 50 00 00 00 |h2.http/1.1uP...|
000000d0 0b 00 02 01 00 00 33 00 2b 00 29 0a 0a 00 01 00 |......3.+.).....|
000000e0 00 1d 00 20 2f e5 7d a3 47 cd 62 43 15 28 da ac |... /.}.G.bC.(..|
000000f0 5f bb 29 07 30 ff f6 84 af c4 cf c2 ed 90 99 5f |_.).0.........._|
00000100 58 cb 3b 74 00 2d 00 02 01 01 00 2b 00 0b 0a 0a |X.;t.-.....+....|
00000110 0a 03 04 03 03 03 02 03 01 00 0a 00 0a 00 08 0a |................|
00000120 0a 00 1d 00 17 00 18 00 1b 00 03 02 00 02 1a 1a |................|
00000130 00 01 00 00 15 00 ce 00 00 00 00 00 00 00 00 00 |................|
00000140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000200 00 00 00 00 00 |.....|
>>> Flow 2 (server to client)
00000000 16 03 03 00 7a 02 00 00 76 03 03 c5 4a de 6c e8 |....z...v...J.l.|
00000010 25 6d db 77 3b ef e6 22 28 0a 19 ec 58 2d ae a1 |%m.w;.."(...X-..|
00000020 57 f6 1b 08 52 c8 5d 79 4b b1 a4 20 00 00 00 00 |W...R.]yK.. ....|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 13 01 00 00 |................|
00000050 2e 00 2b 00 02 03 04 00 33 00 24 00 1d 00 20 df |..+.....3.$... .|
00000060 ec d9 e6 68 f6 90 5d 47 35 4b 5c 4d 2f 91 fe 68 |...h..]G5K\M/..h|
00000070 43 d8 0f f5 1e de 21 03 8e 4f d4 b1 f7 8f 58 14 |C.....!..O....X.|
00000080 03 03 00 01 01 17 03 03 00 17 10 ec 0b 62 a4 0e |.............b..|
00000090 bb 5d ea 09 4a 83 92 13 c8 a7 5e d6 8e 1b 11 91 |.]..J.....^.....|
000000a0 6b 17 03 03 02 6d ff 05 19 3f ec 22 b2 6d 0c ea |k....m...?.".m..|
000000b0 4d 4f 32 26 11 c6 b3 fd 29 04 e5 dc 50 ec ff 1f |MO2&....)...P...|
000000c0 fb af 2e c9 a0 6c 4a 58 17 b1 04 e8 3c bc 62 cd |.....lJX....<.b.|
000000d0 08 c1 42 d1 be 56 5d 70 25 f2 f1 37 be df 9f a7 |..B..V]p%..7....|
000000e0 1b f8 2b f9 3f 1c cc ed 66 d4 06 19 0e f9 ef bc |..+.?...f.......|
000000f0 aa ac c3 89 7e 22 30 5f 8e b7 ca 54 93 ae 29 b3 |....~"0_...T..).|
00000100 5d 9d 9e c2 9c 03 3f 0e 25 1f 9a 73 13 74 18 03 |].....?.%..s.t..|
00000110 94 7d 23 54 5b bd 9c 82 d1 ae cd 42 95 9b 37 a7 |.}#T[......B..7.|
00000120 7f 1c 75 5c 3a 21 5d aa 34 bb 9a d1 26 75 09 a7 |..u\:!].4...&u..|
00000130 4f 63 24 c2 50 90 28 a9 b3 92 61 ac 9f 68 f6 50 |Oc$.P.(...a..h.P|
00000140 eb 16 3f 1a 96 3f 36 2f e8 16 d2 2c 66 ee 80 b7 |..?..?6/...,f...|
00000150 0c 58 bf 0d f4 38 09 cb ad fa aa 22 a0 e7 66 bb |.X...8....."..f.|
00000160 27 7f 57 5c 82 24 f6 b5 af 4f a7 90 ba 60 62 3c |'.W\.$...O...`b<|
00000170 4d 74 b4 33 aa ab 23 07 11 84 3e bd 98 aa d5 42 |Mt.3..#...>....B|
00000180 a8 43 79 58 31 e7 e2 e6 52 bd 30 60 27 f9 da 17 |.CyX1...R.0`'...|
00000190 16 0b 7c 15 14 30 0e ae 11 16 e1 71 c9 cc f8 2f |..|..0.....q.../|
000001a0 91 31 34 09 95 6a 10 4b 04 73 27 77 73 4f 2e 7a |.14..j.K.s'wsO.z|
000001b0 27 b4 79 5e 3a 50 42 60 f8 e0 48 98 a3 b0 5c 6d |'.y^:PB`..H...\m|
000001c0 36 bd 2d 42 3c 82 0b 7e 81 55 a1 74 75 83 80 22 |6.-B<..~.U.tu.."|
000001d0 28 e0 41 4e 7d f5 7f f2 2a 5b b0 7d 39 ab 45 80 |(.AN}...*[.}9.E.|
000001e0 17 4c 41 b5 ec e5 4f 45 e0 ee 6c c0 64 0a 9a c3 |.LA...OE..l.d...|
000001f0 b4 46 df d6 bf 2b 23 96 44 e5 fc 29 9c d3 f0 7f |.F...+#.D..)....|
00000200 dd f9 63 86 07 90 be 0a d4 19 2c 1d 52 db aa 3d |..c.......,.R..=|
00000210 92 0d 07 94 71 f9 78 e6 41 84 4f 94 30 1c b5 ae |....q.x.A.O.0...|
00000220 3f de 5e 95 13 23 4b 43 42 87 68 05 48 41 d6 52 |?.^..#KCB.h.HA.R|
00000230 bc af 11 22 25 4a a3 2a 1c 7e 49 a2 ed 0f c5 27 |..."%J.*.~I....'|
00000240 3c 39 48 5b af a9 49 81 f2 b2 73 e3 dc de d5 fd |<9H[..I...s.....|
00000250 1c 32 a3 c8 7d bb 8c 5e 4c 25 24 a6 ae 86 d0 25 |.2..}..^L%$....%|
00000260 83 cb 38 16 62 27 f3 d8 57 cf eb 2f 27 24 55 c5 |..8.b'..W../'$U.|
00000270 7b 65 7a 25 b4 40 97 18 79 e1 fb 3b b6 12 2e 10 |{ez%.@..y..;....|
00000280 ac 89 21 9d f7 5a 3e 00 65 f8 6f 31 4b 02 2d 9f |..!..Z>.e.o1K.-.|
00000290 07 00 1e 2d 85 27 c1 17 4a 26 78 4a 0e eb ca b6 |...-.'..J&xJ....|
000002a0 32 57 48 7f 87 5a b2 ce 24 03 75 b9 d3 94 48 f9 |2WH..Z..$.u...H.|
000002b0 f4 b1 e8 89 ab 7e c9 75 c6 0d 18 cd c8 3a 05 9f |.....~.u.....:..|
000002c0 8a d9 42 39 30 69 0a d7 56 9d 3b 51 af 13 6a 59 |..B90i..V.;Q..jY|
000002d0 ea 72 e2 75 8a aa 3f 25 49 93 e5 a2 da f6 31 67 |.r.u..?%I.....1g|
000002e0 6a 68 af b4 6e 14 8e 75 91 b5 3d 24 52 11 da 0b |jh..n..u..=$R...|
000002f0 d0 66 db 8b 61 13 c7 93 0d bd bc b9 78 85 fd 12 |.f..a.......x...|
00000300 b5 56 84 3d 7b 75 9f 50 bf d6 a4 eb 8d 24 4c 11 |.V.={u.P.....$L.|
00000310 1c 16 4d 17 03 03 00 99 a8 de 68 0a 8c 77 18 87 |..M.......h..w..|
00000320 24 be eb 40 1d a0 f6 92 f2 ac 90 15 b7 6c 0b cc |$..@.........l..|
00000330 6c 85 54 42 d5 89 91 57 bc dd b6 7f ee a2 e0 6f |l.TB...W.......o|
00000340 17 92 23 cc 73 1e 16 f3 49 8c 82 ef 15 4f 8a be |..#.s...I....O..|
00000350 bd 07 8f 72 5c b1 fc b3 8c e9 a3 e1 ec 9d 7a a2 |...r\.........z.|
00000360 8f 68 1a 91 ab e8 d7 3a 67 b5 bc 8a 72 1f 2d 51 |.h.....:g...r.-Q|
00000370 ba 1a 2b 75 24 3f 0c 8a 79 a9 ac ba 71 da 0c 60 |..+u$?..y...q..`|
00000380 55 3b 60 2c 51 18 f8 ab c2 45 33 74 76 2e 2c eb |U;`,Q....E3tv.,.|
00000390 d0 d5 36 10 5a ee 88 2a 96 15 19 f3 aa ba b9 71 |..6.Z..*.......q|
000003a0 e6 40 42 a0 92 3a 76 22 de a6 f9 49 22 35 c2 56 |.@B..:v"...I"5.V|
000003b0 b2 17 03 03 00 35 5d 29 4e e1 67 87 54 08 21 98 |.....5])N.g.T.!.|
000003c0 a1 dd 93 e9 22 d2 4f 65 5e 41 99 1d 7e 72 c0 20 |....".Oe^A..~r. |
000003d0 2b f9 67 7a 79 68 3a 00 b1 99 39 fe 33 27 5e 4b |+.gzyh:...9.3'^K|
000003e0 67 aa fd 0e 3c b6 1f fd cb 91 e1 |g...<......|
>>> Flow 3 (client to server)
00000000 14 03 03 00 01 01 17 03 03 00 35 2c 4a 03 fc 16 |..........5,J...|
00000010 01 d8 10 25 27 1e 2d 0e b8 62 9c b4 f3 bd 50 a2 |...%'.-..b....P.|
00000020 d1 e7 9e 5f cb 2c d5 ce 5a 56 c5 50 ab 12 4b 69 |..._.,..ZV.P..Ki|
00000030 4c e3 eb 20 ca 91 e0 07 9c b4 6c 5b 79 d3 c5 a4 |L.. ......l[y...|
00000040 17 03 03 00 17 12 d5 76 2d 12 23 20 e5 a4 35 45 |.......v-.# ..5E|
00000050 7d 5a e1 bc a3 da 08 11 5e a5 3b 78 17 03 03 00 |}Z......^.;x....|
00000060 13 d7 07 1e 17 ef df 5d da 27 29 52 5a 9a ed a5 |.......].')RZ...|
00000070 07 1b 81 f1 |....|

View file

@ -0,0 +1,107 @@
>>> Flow 1 (client to server)
00000000 16 03 01 02 00 01 00 01 fc 03 03 00 00 00 00 00 |................|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 20 00 00 00 00 |........... ....|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 24 13 01 |.............$..|
00000050 13 03 13 02 c0 2b c0 2f cc a9 cc a8 c0 2c c0 30 |.....+./.....,.0|
00000060 c0 0a c0 09 c0 13 c0 14 00 9c 00 9d 00 2f 00 35 |............./.5|
00000070 00 0a 01 00 01 8f 00 00 00 05 00 03 00 00 00 00 |................|
00000080 17 00 00 ff 01 00 01 00 00 0a 00 0e 00 0c 00 1d |................|
00000090 00 17 00 18 00 19 01 00 01 01 00 0b 00 02 01 00 |................|
000000a0 00 10 00 0e 00 0c 02 68 32 08 68 74 74 70 2f 31 |.......h2.http/1|
000000b0 2e 31 00 05 00 05 01 00 00 00 00 00 33 00 6b 00 |.1..........3.k.|
000000c0 69 00 1d 00 20 2f e5 7d a3 47 cd 62 43 15 28 da |i... /.}.G.bC.(.|
000000d0 ac 5f bb 29 07 30 ff f6 84 af c4 cf c2 ed 90 99 |._.).0..........|
000000e0 5f 58 cb 3b 74 00 17 00 41 04 1e 18 37 ef 0d 19 |_X.;t...A...7...|
000000f0 51 88 35 75 71 b5 e5 54 5b 12 2e 8f 09 67 fd a7 |Q.5uq..T[....g..|
00000100 24 20 3e b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e f1 |$ >.V...(^.+-O..|
00000110 07 9f 6c 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 a6 |..lK[.V.2B.X..I.|
00000120 b5 68 1a 41 03 56 6b dc 5a 89 00 2b 00 05 04 03 |.h.A.Vk.Z..+....|
00000130 04 03 03 00 0d 00 18 00 16 04 03 05 03 06 03 08 |................|
00000140 04 08 05 08 06 04 01 05 01 06 01 02 03 02 01 00 |................|
00000150 1c 00 02 40 01 00 15 00 ac 00 00 00 00 00 00 00 |...@............|
00000160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000200 00 00 00 00 00 |.....|
>>> Flow 2 (server to client)
00000000 16 03 03 00 7a 02 00 00 76 03 03 0c f4 d3 9a 51 |....z...v......Q|
00000010 ee f9 a7 5d ca 63 bc d1 41 e4 18 b3 11 4e b7 27 |...].c..A....N.'|
00000020 b2 81 9d ff 98 da 3c 9a 5c 0b 07 20 00 00 00 00 |......<.\.. ....|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 13 01 00 00 |................|
00000050 2e 00 2b 00 02 03 04 00 33 00 24 00 1d 00 20 3d |..+.....3.$... =|
00000060 58 17 cf 6c 8d 8a 8f f3 79 a9 c4 c9 3f 5e 15 f8 |X..l....y...?^..|
00000070 49 6a e0 61 be 0d 62 fd 20 6a ad 9d 66 71 57 14 |Ij.a..b. j..fqW.|
00000080 03 03 00 01 01 17 03 03 00 17 26 47 66 c9 a5 1d |..........&Gf...|
00000090 0c 10 9f 98 a7 47 33 8b ef aa 90 41 13 1c 34 b3 |.....G3....A..4.|
000000a0 ed 17 03 03 02 6d 8e 5f cb 3d 6f 38 a4 96 a4 7f |.....m._.=o8....|
000000b0 64 f4 7b 90 a8 83 f7 a9 42 ff 86 dd 18 5e aa 5f |d.{.....B....^._|
000000c0 01 0d 73 8f 4c 9e d2 f9 e2 c1 83 ec c5 cf 7a 6a |..s.L.........zj|
000000d0 20 2e 1f 81 55 0b cb e4 2e f9 5a cf 2d 13 5b 8c | ...U.....Z.-.[.|
000000e0 4b 15 3f ac 5c 35 87 60 a7 53 33 93 d9 8c b6 19 |K.?.\5.`.S3.....|
000000f0 cf 4f cb 18 0d 53 e1 11 3d f7 c0 f0 65 1c 1f 7a |.O...S..=...e..z|
00000100 92 fd 9f bb 93 08 b5 68 29 3a 5c b0 24 7a e9 37 |.......h):\.$z.7|
00000110 a8 e0 e8 a1 fd c2 34 d4 2f ae 48 dc 4d 78 95 dd |......4./.H.Mx..|
00000120 ae fe 88 02 74 bc d9 03 13 11 49 d1 ba b1 fb 49 |....t.....I....I|
00000130 92 8e ae ef 43 3e 19 85 70 46 28 20 b4 4b 6b 4e |....C>..pF( .KkN|
00000140 b1 8f a8 4b ce 48 4c 6f f0 12 5c 10 de 9e 37 d7 |...K.HLo..\...7.|
00000150 82 f3 f7 5e aa 3f 25 0b 74 e7 fa 7a 26 7b 88 c1 |...^.?%.t..z&{..|
00000160 a9 df cb 70 98 91 4a 4a ec f3 62 ef 13 42 71 5a |...p..JJ..b..BqZ|
00000170 ca ca 59 c6 4b c0 8b 0b 82 51 14 11 8c 7a c0 d6 |..Y.K....Q...z..|
00000180 42 5a 1d 7c ff d7 5e de 6e 99 f8 ee 55 aa 8f 27 |BZ.|..^.n...U..'|
00000190 b1 c0 65 a8 52 74 42 35 0d e5 3e 0e 0e 0c c4 a3 |..e.RtB5..>.....|
000001a0 32 63 1d 72 57 55 f1 a5 d5 f9 95 99 31 1c 09 d6 |2c.rWU......1...|
000001b0 48 58 cf 6e f7 8d a9 a7 c6 74 91 77 6c 28 13 65 |HX.n.....t.wl(.e|
000001c0 b8 53 f8 4b e1 1b 32 39 31 36 41 d2 35 4c 7b 82 |.S.K..2916A.5L{.|
000001d0 75 bc a6 66 b3 d2 df 51 c8 da 65 7e 94 93 0d e8 |u..f...Q..e~....|
000001e0 33 4c f8 c6 16 50 12 c7 06 0c 9e f9 57 36 3c e3 |3L...P......W6<.|
000001f0 94 ba 50 c7 ab 27 33 cb 7b 82 ce fc 39 1a c8 7f |..P..'3.{...9...|
00000200 59 85 d7 3c 37 ff fc b8 03 2b da 62 80 d3 c0 c6 |Y..<7....+.b....|
00000210 96 d0 fc 50 08 7e 02 25 c7 ca b6 6a 27 cc b8 d2 |...P.~.%...j'...|
00000220 58 15 18 58 01 67 10 c8 a0 0b b1 39 34 ce fe 64 |X..X.g.....94..d|
00000230 25 22 68 39 82 a7 21 ab e2 41 c6 35 dd 40 5c e7 |%"h9..!..A.5.@\.|
00000240 88 e5 b0 9e 8d 20 2a 9a f2 f7 6e d0 6e fe 06 9f |..... *...n.n...|
00000250 9f b4 56 e8 e4 1d cd 55 5c 68 6e 8d 40 77 e6 d5 |..V....U\hn.@w..|
00000260 98 73 8f d2 53 34 1c 15 aa ad 27 51 ef 31 b2 0f |.s..S4....'Q.1..|
00000270 94 d5 69 1f 70 ec e3 ac 0c cc a3 a1 d7 61 ee 1e |..i.p........a..|
00000280 15 83 bd 41 9f 4c 2b d3 cc d8 06 d8 d8 a4 d3 64 |...A.L+........d|
00000290 95 49 e4 03 73 54 f6 f7 24 3d c3 a6 f3 6a 19 dc |.I..sT..$=...j..|
000002a0 a3 00 00 7e 56 f2 f9 26 5f 09 ca c5 b0 eb 58 d8 |...~V..&_.....X.|
000002b0 22 93 c9 ce 5d f7 63 72 7d 72 56 77 38 08 f9 86 |"...].cr}rVw8...|
000002c0 8e 8f ad 8c 02 64 36 fe b8 09 d7 0e 58 73 aa 99 |.....d6.....Xs..|
000002d0 d1 d1 60 4c 65 5f 80 ed 13 0d a0 19 b4 b5 d3 8a |..`Le_..........|
000002e0 ba dc 84 94 af 7a 77 61 b0 bb 01 38 d2 6d 82 6d |.....zwa...8.m.m|
000002f0 22 08 17 17 16 0c 48 25 c3 b9 35 ff 78 7b ab af |".....H%..5.x{..|
00000300 63 9a d9 83 d1 65 2a 3e 7f b0 90 dd 67 c1 be 0a |c....e*>....g...|
00000310 14 e5 17 17 03 03 00 99 ff 0b c2 4f 7d 1f 7a 8e |...........O}.z.|
00000320 15 a1 53 35 e1 bd c7 20 ad 87 e9 22 99 0b 46 76 |..S5... ..."..Fv|
00000330 1f 9d 87 6c 64 ef 5f 34 66 fa c5 6a af bd c5 34 |...ld._4f..j...4|
00000340 c8 32 f4 c4 06 3e 85 b4 41 e9 1a e8 98 c1 69 20 |.2...>..A.....i |
00000350 3e ec 84 ae 63 27 98 e5 81 07 89 8b d5 74 6f b1 |>...c'.......to.|
00000360 80 b9 fb e0 f8 d8 ef 3e 00 d8 57 97 41 87 26 02 |.......>..W.A.&.|
00000370 a9 58 83 5a 5f ad 3c 1f ed 24 50 fe a3 3b 70 be |.X.Z_.<..$P..;p.|
00000380 bd 03 19 d6 6a 46 72 7a 68 36 bb 40 e5 88 bc 1e |....jFrzh6.@....|
00000390 cc 02 3d 09 0b 59 be 40 71 44 0c 34 17 40 bf 46 |..=..Y.@qD.4.@.F|
000003a0 c6 6c 01 05 bd a1 83 e7 ad 12 1c 9d 78 c5 08 a2 |.l..........x...|
000003b0 26 17 03 03 00 35 0b c5 51 ec 7b c5 96 9d 93 b6 |&....5..Q.{.....|
000003c0 1e 71 3f 2d 34 32 50 c1 77 08 ce e9 83 59 47 da |.q?-42P.w....YG.|
000003d0 b5 8c 5e cf 58 89 da 98 c4 b8 73 c0 1c f6 30 bd |..^.X.....s...0.|
000003e0 c2 b1 34 7a 1a 54 ad ef c2 2b 2b |..4z.T...++|
>>> Flow 3 (client to server)
00000000 14 03 03 00 01 01 17 03 03 00 35 5e 98 29 74 04 |..........5^.)t.|
00000010 bb f5 5e 17 dc 36 8f cb 3c e0 45 1a d2 54 ae f6 |..^..6..<.E..T..|
00000020 3a 36 59 c0 fa dc 2d e7 73 66 ca af d5 c7 e6 0d |:6Y...-.sf......|
00000030 e1 23 17 a3 88 85 16 83 16 ec 78 d3 78 bc 39 ba |.#........x.x.9.|
00000040 17 03 03 00 17 91 53 7f ab 3a 4a a0 d2 e5 0a 7e |......S..:J....~|
00000050 e9 52 2a fa de f8 f8 20 76 ad 9f 48 17 03 03 00 |.R*.... v..H....|
00000060 13 0d 74 fc f8 27 c1 fd 06 b1 3a 5c 2d 37 63 dd |..t..'....:\-7c.|
00000070 5c 59 28 9a |\Y(.|

View file

@ -162,6 +162,20 @@ var (
// https://tools.ietf.org/html/draft-ietf-tls-grease-01 // https://tools.ietf.org/html/draft-ietf-tls-grease-01
const GREASE_PLACEHOLDER = 0x0a0a const GREASE_PLACEHOLDER = 0x0a0a
func isGREASEUint16(v uint16) bool {
// First byte is same as second byte
// and lowest nibble is 0xa
return ((v >> 8) == v&0xff) && v&0xf == 0xa
}
func unGREASEUint16(v uint16) uint16 {
if isGREASEUint16(v) {
return GREASE_PLACEHOLDER
} else {
return v
}
}
// utlsMacSHA384 returns a SHA-384 based MAC. These are only supported in TLS 1.2 // utlsMacSHA384 returns a SHA-384 based MAC. These are only supported in TLS 1.2
// so the given version is ignored. // so the given version is ignored.
func utlsMacSHA384(version uint16, key []byte) macFunction { func utlsMacSHA384(version uint16, key []byte) macFunction {

33
u_common_test.go Normal file
View file

@ -0,0 +1,33 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tls
import (
"testing"
)
func TestUTLSIsGrease(t *testing.T) {
var testMap = []struct {
version uint16
isGREASE bool
}{
{0x0a0a, true},
{0x1a1a, true},
{0x2a1a, false},
{0x2a2a, true},
{0x1234, false},
{0x1a2a, false},
{0xdeed, false},
{0xb1b1, false},
{0x0b0b, false},
}
for _, testCase := range testMap {
if isGREASEUint16(testCase.version) != testCase.isGREASE {
t.Errorf("misidentified GREASE: testing %x, isGREASE: %v", testCase.version, isGREASEUint16(testCase.version))
}
}
}

View file

@ -17,6 +17,28 @@ import (
"time" "time"
) )
// helloStrategy is a sum type interface which allows us to pass either a ClientHelloID or a ClientHelloSpec and then act accordingly
type helloStrategy interface {
helloName() string
}
type helloID struct {
id ClientHelloID
}
func (hid *helloID) helloName() string {
return hid.id.Str()
}
type helloSpec struct {
name string
spec *ClientHelloSpec
}
func (hs *helloSpec) helloName() string {
return hs.name
}
func TestUTLSMarshalNoOp(t *testing.T) { func TestUTLSMarshalNoOp(t *testing.T) {
str := "We rely on clientHelloMsg.marshal() not doing anything if clientHelloMsg.raw is set" str := "We rely on clientHelloMsg.marshal() not doing anything if clientHelloMsg.raw is set"
uconn := UClient(&net.TCPConn{}, &Config{ServerName: "foobar"}, HelloGolang) uconn := UClient(&net.TCPConn{}, &Config{ServerName: "foobar"}, HelloGolang)
@ -32,146 +54,146 @@ func TestUTLSMarshalNoOp(t *testing.T) {
} }
func TestUTLSHandshakeClientParrotGolang(t *testing.T) { func TestUTLSHandshakeClientParrotGolang(t *testing.T) {
helloID := HelloGolang hello := &helloID{HelloGolang}
testUTLSHandshakeClientECDHE_ECDSA_WITH_CHACHA20_POLY1305(t, helloID) testUTLSHandshakeClientECDHE_ECDSA_WITH_CHACHA20_POLY1305(t, hello)
testUTLSHandshakeClientECDHE_RSA_WITH_CHACHA20_POLY1305(t, helloID) testUTLSHandshakeClientECDHE_RSA_WITH_CHACHA20_POLY1305(t, hello)
testUTLSHandshakeClientECDHE_RSA_AES128_GCM_SHA256(t, helloID) testUTLSHandshakeClientECDHE_RSA_AES128_GCM_SHA256(t, hello)
testUTLSHandshakeClientECDHE_ECDSA_AES128_GCM_SHA256(t, helloID) testUTLSHandshakeClientECDHE_ECDSA_AES128_GCM_SHA256(t, hello)
testUTLSHandshakeClientECDHE_RSA_AES256_CBC_SHA(t, helloID) testUTLSHandshakeClientECDHE_RSA_AES256_CBC_SHA(t, hello)
testUTLSHandshakeClientECDHE_ECDSA_AES256_CBC_SHA(t, helloID) testUTLSHandshakeClientECDHE_ECDSA_AES256_CBC_SHA(t, hello)
testUTLSHandshakeClientECDHE_RSA_AES128_CBC_SHA(t, helloID) testUTLSHandshakeClientECDHE_RSA_AES128_CBC_SHA(t, hello)
testUTLSHandshakeClientECDHE_ECDSA_AES128_CBC_SHA(t, helloID) testUTLSHandshakeClientECDHE_ECDSA_AES128_CBC_SHA(t, hello)
testUTLSHandshakeClientRSA_AES128_GCM_SHA256(t, helloID) testUTLSHandshakeClientRSA_AES128_GCM_SHA256(t, hello)
} }
func TestUTLSHandshakeClientParrotChrome_70(t *testing.T) { func TestUTLSHandshakeClientParrotChrome_70(t *testing.T) {
helloID := HelloChrome_70 hello := &helloID{HelloChrome_70}
testUTLSHandshakeClientTLS13_AES_128_GCM_SHA256(t, helloID) testUTLSHandshakeClientTLS13_AES_128_GCM_SHA256(t, hello)
testUTLSHandshakeClientTLS13_AES_256_GCM_SHA384(t, helloID) testUTLSHandshakeClientTLS13_AES_256_GCM_SHA384(t, hello)
testUTLSHandshakeClientTLS13_CHACHA20_POLY1305_SHA256(t, helloID) testUTLSHandshakeClientTLS13_CHACHA20_POLY1305_SHA256(t, hello)
//testUTLSHandshakeClientECDHE_ECDSA_AES128_GCM_SHA256(t, helloID) //testUTLSHandshakeClientECDHE_ECDSA_AES128_GCM_SHA256(t, hello)
testUTLSHandshakeClientECDHE_RSA_AES128_GCM_SHA256(t, helloID) testUTLSHandshakeClientECDHE_RSA_AES128_GCM_SHA256(t, hello)
//testUTLSHandshakeClientECDHE_ECDSA_AES256_GCM_SHA256(t, helloID) //testUTLSHandshakeClientECDHE_ECDSA_AES256_GCM_SHA256(t, hello)
testUTLSHandshakeClientECDHE_RSA_AES256_GCM_SHA256(t, helloID) testUTLSHandshakeClientECDHE_RSA_AES256_GCM_SHA256(t, hello)
//testUTLSHandshakeClientECDHE_ECDSA_WITH_CHACHA20_POLY1305(t, helloID) //testUTLSHandshakeClientECDHE_ECDSA_WITH_CHACHA20_POLY1305(t, hello)
testUTLSHandshakeClientECDHE_RSA_WITH_CHACHA20_POLY1305(t, helloID) testUTLSHandshakeClientECDHE_RSA_WITH_CHACHA20_POLY1305(t, hello)
testUTLSHandshakeClientECDHE_RSA_AES128_CBC_SHA(t, helloID) testUTLSHandshakeClientECDHE_RSA_AES128_CBC_SHA(t, hello)
testUTLSHandshakeClientECDHE_RSA_AES256_CBC_SHA(t, helloID) testUTLSHandshakeClientECDHE_RSA_AES256_CBC_SHA(t, hello)
testUTLSHandshakeClientRSA_AES128_GCM_SHA256(t, helloID) testUTLSHandshakeClientRSA_AES128_GCM_SHA256(t, hello)
} }
func TestUTLSHandshakeClientParrotChrome_58(t *testing.T) { func TestUTLSHandshakeClientParrotChrome_58(t *testing.T) {
helloID := HelloChrome_58 hello := &helloID{HelloChrome_58}
// TODO: EC tests below are disabled because latest version of reference OpenSSL doesn't support p256 nor p384 // TODO: EC tests below are disabled because latest version of reference OpenSSL doesn't support p256 nor p384
// nor X25519 and I can't find configuration flag to enable it. Therefore I can't record replays. // nor X25519 and I can't find configuration flag to enable it. Therefore I can't record replays.
//testUTLSHandshakeClientECDHE_ECDSA_AES128_GCM_SHA256(t, helloID) //testUTLSHandshakeClientECDHE_ECDSA_AES128_GCM_SHA256(t, hello)
testUTLSHandshakeClientECDHE_RSA_AES128_GCM_SHA256(t, helloID) testUTLSHandshakeClientECDHE_RSA_AES128_GCM_SHA256(t, hello)
//testUTLSHandshakeClientECDHE_ECDSA_AES256_GCM_SHA256(t, helloID) //testUTLSHandshakeClientECDHE_ECDSA_AES256_GCM_SHA256(t, hello)
testUTLSHandshakeClientECDHE_RSA_AES256_GCM_SHA256(t, helloID) testUTLSHandshakeClientECDHE_RSA_AES256_GCM_SHA256(t, hello)
//testUTLSHandshakeClientECDHE_ECDSA_WITH_CHACHA20_POLY1305(t, helloID) //testUTLSHandshakeClientECDHE_ECDSA_WITH_CHACHA20_POLY1305(t, hello)
testUTLSHandshakeClientECDHE_RSA_WITH_CHACHA20_POLY1305(t, helloID) testUTLSHandshakeClientECDHE_RSA_WITH_CHACHA20_POLY1305(t, hello)
testUTLSHandshakeClientECDHE_RSA_AES128_CBC_SHA(t, helloID) testUTLSHandshakeClientECDHE_RSA_AES128_CBC_SHA(t, hello)
testUTLSHandshakeClientECDHE_RSA_AES256_CBC_SHA(t, helloID) testUTLSHandshakeClientECDHE_RSA_AES256_CBC_SHA(t, hello)
testUTLSHandshakeClientRSA_AES128_GCM_SHA256(t, helloID) testUTLSHandshakeClientRSA_AES128_GCM_SHA256(t, hello)
} }
func TestUTLSHandshakeClientParrotFirefox_63(t *testing.T) { func TestUTLSHandshakeClientParrotFirefox_63(t *testing.T) {
helloID := HelloFirefox_63 hello := &helloID{HelloFirefox_63}
testUTLSHandshakeClientTLS13_AES_128_GCM_SHA256(t, helloID) testUTLSHandshakeClientTLS13_AES_128_GCM_SHA256(t, hello)
testUTLSHandshakeClientTLS13_AES_256_GCM_SHA384(t, helloID) testUTLSHandshakeClientTLS13_AES_256_GCM_SHA384(t, hello)
testUTLSHandshakeClientTLS13_CHACHA20_POLY1305_SHA256(t, helloID) testUTLSHandshakeClientTLS13_CHACHA20_POLY1305_SHA256(t, hello)
testUTLSHandshakeClientECDHE_ECDSA_AES128_GCM_SHA256(t, helloID) testUTLSHandshakeClientECDHE_ECDSA_AES128_GCM_SHA256(t, hello)
testUTLSHandshakeClientECDHE_RSA_AES128_GCM_SHA256(t, helloID) testUTLSHandshakeClientECDHE_RSA_AES128_GCM_SHA256(t, hello)
testUTLSHandshakeClientECDHE_ECDSA_WITH_CHACHA20_POLY1305(t, helloID) testUTLSHandshakeClientECDHE_ECDSA_WITH_CHACHA20_POLY1305(t, hello)
testUTLSHandshakeClientECDHE_RSA_WITH_CHACHA20_POLY1305(t, helloID) testUTLSHandshakeClientECDHE_RSA_WITH_CHACHA20_POLY1305(t, hello)
//testUTLSHandshakeClientECDHE_ECDSA_AES256_GCM_SHA256(t, helloID) TODO: enable when OpenSSL supports it //testUTLSHandshakeClientECDHE_ECDSA_AES256_GCM_SHA256(t, hello) TODO: enable when OpenSSL supports it
testUTLSHandshakeClientECDHE_RSA_AES256_GCM_SHA256(t, helloID) testUTLSHandshakeClientECDHE_RSA_AES256_GCM_SHA256(t, hello)
testUTLSHandshakeClientECDHE_ECDSA_AES256_CBC_SHA(t, helloID) testUTLSHandshakeClientECDHE_ECDSA_AES256_CBC_SHA(t, hello)
testUTLSHandshakeClientECDHE_ECDSA_AES128_CBC_SHA(t, helloID) testUTLSHandshakeClientECDHE_ECDSA_AES128_CBC_SHA(t, hello)
testUTLSHandshakeClientECDHE_RSA_AES256_CBC_SHA(t, helloID) testUTLSHandshakeClientECDHE_RSA_AES256_CBC_SHA(t, hello)
testUTLSHandshakeClientECDHE_RSA_AES128_CBC_SHA(t, helloID) testUTLSHandshakeClientECDHE_RSA_AES128_CBC_SHA(t, hello)
} }
func TestUTLSHandshakeClientParrotFirefox_55(t *testing.T) { func TestUTLSHandshakeClientParrotFirefox_55(t *testing.T) {
helloID := HelloFirefox_55 hello := &helloID{HelloFirefox_55}
testUTLSHandshakeClientECDHE_ECDSA_AES128_GCM_SHA256(t, helloID) testUTLSHandshakeClientECDHE_ECDSA_AES128_GCM_SHA256(t, hello)
testUTLSHandshakeClientECDHE_RSA_AES128_GCM_SHA256(t, helloID) testUTLSHandshakeClientECDHE_RSA_AES128_GCM_SHA256(t, hello)
testUTLSHandshakeClientECDHE_ECDSA_WITH_CHACHA20_POLY1305(t, helloID) testUTLSHandshakeClientECDHE_ECDSA_WITH_CHACHA20_POLY1305(t, hello)
testUTLSHandshakeClientECDHE_RSA_WITH_CHACHA20_POLY1305(t, helloID) testUTLSHandshakeClientECDHE_RSA_WITH_CHACHA20_POLY1305(t, hello)
//testUTLSHandshakeClientECDHE_ECDSA_AES256_GCM_SHA256(t, helloID) TODO: enable when OpenSSL supports it //testUTLSHandshakeClientECDHE_ECDSA_AES256_GCM_SHA256(t, hello) TODO: enable when OpenSSL supports it
testUTLSHandshakeClientECDHE_RSA_AES256_GCM_SHA256(t, helloID) testUTLSHandshakeClientECDHE_RSA_AES256_GCM_SHA256(t, hello)
testUTLSHandshakeClientECDHE_ECDSA_AES256_CBC_SHA(t, helloID) testUTLSHandshakeClientECDHE_ECDSA_AES256_CBC_SHA(t, hello)
testUTLSHandshakeClientECDHE_ECDSA_AES128_CBC_SHA(t, helloID) testUTLSHandshakeClientECDHE_ECDSA_AES128_CBC_SHA(t, hello)
testUTLSHandshakeClientECDHE_RSA_AES256_CBC_SHA(t, helloID) testUTLSHandshakeClientECDHE_RSA_AES256_CBC_SHA(t, hello)
testUTLSHandshakeClientECDHE_RSA_AES128_CBC_SHA(t, helloID) testUTLSHandshakeClientECDHE_RSA_AES128_CBC_SHA(t, hello)
} }
func TestUTLSHandshakeClientParrotChrome_58_setclienthello(t *testing.T) { func TestUTLSHandshakeClientParrotChrome_58_setclienthello(t *testing.T) {
helloID := HelloChrome_58 hello := &helloID{HelloChrome_58}
config := getUTLSTestConfig() config := getUTLSTestConfig()
opensslCipherName := "ECDHE-RSA-AES128-GCM-SHA256" opensslCipherName := "ECDHE-RSA-AES128-GCM-SHA256"
test := &clientTest{ test := &clientTest{
name: "UTLS-setclienthello-" + opensslCipherName + "-" + helloID.Str(), name: "UTLS-setclienthello-" + opensslCipherName + "-" + hello.helloName(),
args: []string{"-cipher", opensslCipherName}, args: []string{"-cipher", opensslCipherName},
config: config, config: config,
} }
runUTLSClientTestTLS12(t, test, helloID) runUTLSClientTestTLS12(t, test, hello)
} }
// tests consistency of fingerprint after HelloRetryRequest // tests consistency of fingerprint after HelloRetryRequest
// chrome 70 is used, due to only specifying X25519 in keyshare, but being able to generate P-256 curve too // chrome 70 is used, due to only specifying X25519 in keyshare, but being able to generate P-256 curve too
// openssl server, configured to use P-256, will send HelloRetryRequest // openssl server, configured to use P-256, will send HelloRetryRequest
func TestUTLSHelloRetryRequest(t *testing.T) { func TestUTLSHelloRetryRequest(t *testing.T) {
helloID := HelloChrome_70 hello := &helloID{HelloChrome_70}
config := testConfig.Clone() config := testConfig.Clone()
config.CurvePreferences = []CurveID{X25519, CurveP256} config.CurvePreferences = []CurveID{X25519, CurveP256}
test := &clientTest{ test := &clientTest{
name: "UTLS-HelloRetryRequest-" + helloID.Str(), name: "UTLS-HelloRetryRequest-" + hello.helloName(),
args: []string{"-cipher", "ECDHE-RSA-AES128-GCM-SHA256", "-curves", "P-256"}, args: []string{"-cipher", "ECDHE-RSA-AES128-GCM-SHA256", "-curves", "P-256"},
config: config, config: config,
} }
runUTLSClientTestTLS13(t, test, helloID) runUTLSClientTestTLS13(t, test, hello)
} }
func TestUTLSRemoveSNIExtension(t *testing.T) { func TestUTLSRemoveSNIExtension(t *testing.T) {
helloID := HelloChrome_70 hello := &helloID{HelloChrome_70}
config := getUTLSTestConfig() config := getUTLSTestConfig()
opensslCipherName := "ECDHE-RSA-AES128-GCM-SHA256" opensslCipherName := "ECDHE-RSA-AES128-GCM-SHA256"
test := &clientTest{ test := &clientTest{
name: "UTLS-" + opensslCipherName + "-" + helloID.Str() + "-OmitSNI", name: "UTLS-" + opensslCipherName + "-" + hello.helloName() + "-OmitSNI",
args: []string{"-cipher", opensslCipherName}, args: []string{"-cipher", opensslCipherName},
config: config, config: config,
} }
runUTLSClientTestForVersion(t, test, "TLSv12-", "-tls1_2", helloID, true) runUTLSClientTestForVersion(t, test, "TLSv12-", "-tls1_2", hello, true)
} }
/* /*
@ -194,192 +216,192 @@ func getUTLSTestConfig() *Config {
return testUTLSConfig return testUTLSConfig
} }
func testUTLSHandshakeClientECDHE_RSA_AES128_CBC_SHA(t *testing.T, helloID ClientHelloID) { func testUTLSHandshakeClientECDHE_RSA_AES128_CBC_SHA(t *testing.T, hello helloStrategy) {
config := getUTLSTestConfig() config := getUTLSTestConfig()
opensslCipherName := "ECDHE-RSA-AES128-SHA" opensslCipherName := "ECDHE-RSA-AES128-SHA"
test := &clientTest{ test := &clientTest{
name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), name: "UTLS-" + opensslCipherName + "-" + hello.helloName(),
args: []string{"-cipher", opensslCipherName}, args: []string{"-cipher", opensslCipherName},
config: config, config: config,
} }
runUTLSClientTestTLS12(t, test, helloID) runUTLSClientTestTLS12(t, test, hello)
} }
func testUTLSHandshakeClientECDHE_RSA_AES256_CBC_SHA(t *testing.T, helloID ClientHelloID) { func testUTLSHandshakeClientECDHE_RSA_AES256_CBC_SHA(t *testing.T, hello helloStrategy) {
config := getUTLSTestConfig() config := getUTLSTestConfig()
opensslCipherName := "ECDHE-RSA-AES256-SHA" opensslCipherName := "ECDHE-RSA-AES256-SHA"
test := &clientTest{ test := &clientTest{
name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), name: "UTLS-" + opensslCipherName + "-" + hello.helloName(),
args: []string{"-cipher", opensslCipherName}, args: []string{"-cipher", opensslCipherName},
config: config, config: config,
} }
runUTLSClientTestTLS12(t, test, helloID) runUTLSClientTestTLS12(t, test, hello)
} }
func testUTLSHandshakeClientECDHE_ECDSA_AES128_CBC_SHA(t *testing.T, helloID ClientHelloID) { func testUTLSHandshakeClientECDHE_ECDSA_AES128_CBC_SHA(t *testing.T, hello helloStrategy) {
config := getUTLSTestConfig() config := getUTLSTestConfig()
opensslCipherName := "ECDHE-ECDSA-AES128-SHA" opensslCipherName := "ECDHE-ECDSA-AES128-SHA"
test := &clientTest{ test := &clientTest{
name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), name: "UTLS-" + opensslCipherName + "-" + hello.helloName(),
args: []string{"-cipher", opensslCipherName}, args: []string{"-cipher", opensslCipherName},
cert: testECDSACertificate, cert: testECDSACertificate,
key: testECDSAPrivateKey, key: testECDSAPrivateKey,
config: config, config: config,
} }
runUTLSClientTestTLS12(t, test, helloID) runUTLSClientTestTLS12(t, test, hello)
} }
func testUTLSHandshakeClientECDHE_ECDSA_AES256_CBC_SHA(t *testing.T, helloID ClientHelloID) { func testUTLSHandshakeClientECDHE_ECDSA_AES256_CBC_SHA(t *testing.T, hello helloStrategy) {
config := getUTLSTestConfig() config := getUTLSTestConfig()
opensslCipherName := "ECDHE-ECDSA-AES256-SHA" opensslCipherName := "ECDHE-ECDSA-AES256-SHA"
test := &clientTest{ test := &clientTest{
name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), name: "UTLS-" + opensslCipherName + "-" + hello.helloName(),
args: []string{"-cipher", opensslCipherName}, args: []string{"-cipher", opensslCipherName},
cert: testECDSACertificate, cert: testECDSACertificate,
key: testECDSAPrivateKey, key: testECDSAPrivateKey,
config: config, config: config,
} }
runUTLSClientTestTLS12(t, test, helloID) runUTLSClientTestTLS12(t, test, hello)
} }
func testUTLSHandshakeClientRSA_AES128_GCM_SHA256(t *testing.T, helloID ClientHelloID) { func testUTLSHandshakeClientRSA_AES128_GCM_SHA256(t *testing.T, hello helloStrategy) {
config := getUTLSTestConfig() config := getUTLSTestConfig()
opensslCipherName := "AES128-GCM-SHA256" opensslCipherName := "AES128-GCM-SHA256"
test := &clientTest{ test := &clientTest{
name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), name: "UTLS-" + opensslCipherName + "-" + hello.helloName(),
args: []string{"-cipher", opensslCipherName}, args: []string{"-cipher", opensslCipherName},
config: config, config: config,
} }
runUTLSClientTestTLS12(t, test, helloID) runUTLSClientTestTLS12(t, test, hello)
} }
func testUTLSHandshakeClientECDHE_ECDSA_AES128_GCM_SHA256(t *testing.T, helloID ClientHelloID) { func testUTLSHandshakeClientECDHE_ECDSA_AES128_GCM_SHA256(t *testing.T, hello helloStrategy) {
config := getUTLSTestConfig() config := getUTLSTestConfig()
opensslCipherName := "ECDHE-ECDSA-AES128-GCM-SHA256" opensslCipherName := "ECDHE-ECDSA-AES128-GCM-SHA256"
test := &clientTest{ test := &clientTest{
name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), name: "UTLS-" + opensslCipherName + "-" + hello.helloName(),
args: []string{"-cipher", opensslCipherName}, args: []string{"-cipher", opensslCipherName},
cert: testECDSACertificate, cert: testECDSACertificate,
key: testECDSAPrivateKey, key: testECDSAPrivateKey,
config: config, config: config,
} }
runUTLSClientTestTLS12(t, test, helloID) runUTLSClientTestTLS12(t, test, hello)
} }
func testUTLSHandshakeClientECDHE_RSA_AES128_GCM_SHA256(t *testing.T, helloID ClientHelloID) { func testUTLSHandshakeClientECDHE_RSA_AES128_GCM_SHA256(t *testing.T, hello helloStrategy) {
config := getUTLSTestConfig() config := getUTLSTestConfig()
opensslCipherName := "ECDHE-RSA-AES128-GCM-SHA256" opensslCipherName := "ECDHE-RSA-AES128-GCM-SHA256"
test := &clientTest{ test := &clientTest{
name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), name: "UTLS-" + opensslCipherName + "-" + hello.helloName(),
args: []string{"-cipher", opensslCipherName}, args: []string{"-cipher", opensslCipherName},
config: config, config: config,
} }
runUTLSClientTestTLS12(t, test, helloID) runUTLSClientTestTLS12(t, test, hello)
} }
func testUTLSHandshakeClientECDHE_ECDSA_AES256_GCM_SHA256(t *testing.T, helloID ClientHelloID) { func testUTLSHandshakeClientECDHE_ECDSA_AES256_GCM_SHA256(t *testing.T, hello helloStrategy) {
config := getUTLSTestConfig() config := getUTLSTestConfig()
opensslCipherName := "ECDHE-ECDSA-AES256-GCM-SHA256" opensslCipherName := "ECDHE-ECDSA-AES256-GCM-SHA256"
test := &clientTest{ test := &clientTest{
name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), name: "UTLS-" + opensslCipherName + "-" + hello.helloName(),
args: []string{"-cipher", opensslCipherName}, args: []string{"-cipher", opensslCipherName},
cert: testECDSACertificate, cert: testECDSACertificate,
key: testECDSAPrivateKey, key: testECDSAPrivateKey,
config: config, config: config,
} }
runUTLSClientTestTLS12(t, test, helloID) runUTLSClientTestTLS12(t, test, hello)
} }
func testUTLSHandshakeClientECDHE_RSA_AES256_GCM_SHA256(t *testing.T, helloID ClientHelloID) { func testUTLSHandshakeClientECDHE_RSA_AES256_GCM_SHA256(t *testing.T, hello helloStrategy) {
config := getUTLSTestConfig() config := getUTLSTestConfig()
opensslCipherName := "ECDHE-RSA-AES128-GCM-SHA256" opensslCipherName := "ECDHE-RSA-AES128-GCM-SHA256"
test := &clientTest{ test := &clientTest{
name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), name: "UTLS-" + opensslCipherName + "-" + hello.helloName(),
args: []string{"-cipher", opensslCipherName}, args: []string{"-cipher", opensslCipherName},
config: config, config: config,
} }
runUTLSClientTestTLS12(t, test, helloID) runUTLSClientTestTLS12(t, test, hello)
} }
func testUTLSHandshakeClientTLS13_AES_128_GCM_SHA256(t *testing.T, helloID ClientHelloID) { func testUTLSHandshakeClientTLS13_AES_128_GCM_SHA256(t *testing.T, hello helloStrategy) {
config := getUTLSTestConfig() config := getUTLSTestConfig()
opensslCipherName := "TLS_AES_128_GCM_SHA256" opensslCipherName := "TLS_AES_128_GCM_SHA256"
test := &clientTest{ test := &clientTest{
name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), name: "UTLS-" + opensslCipherName + "-" + hello.helloName(),
args: []string{"-ciphersuites", opensslCipherName}, args: []string{"-ciphersuites", opensslCipherName},
config: config, config: config,
} }
runUTLSClientTestTLS13(t, test, helloID) runUTLSClientTestTLS13(t, test, hello)
} }
func testUTLSHandshakeClientTLS13_AES_256_GCM_SHA384(t *testing.T, helloID ClientHelloID) { func testUTLSHandshakeClientTLS13_AES_256_GCM_SHA384(t *testing.T, hello helloStrategy) {
config := getUTLSTestConfig() config := getUTLSTestConfig()
opensslCipherName := "TLS_AES_256_GCM_SHA384" opensslCipherName := "TLS_AES_256_GCM_SHA384"
test := &clientTest{ test := &clientTest{
name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), name: "UTLS-" + opensslCipherName + "-" + hello.helloName(),
args: []string{"-ciphersuites", opensslCipherName}, args: []string{"-ciphersuites", opensslCipherName},
config: config, config: config,
} }
runUTLSClientTestTLS13(t, test, helloID) runUTLSClientTestTLS13(t, test, hello)
} }
func testUTLSHandshakeClientTLS13_CHACHA20_POLY1305_SHA256(t *testing.T, helloID ClientHelloID) { func testUTLSHandshakeClientTLS13_CHACHA20_POLY1305_SHA256(t *testing.T, hello helloStrategy) {
config := getUTLSTestConfig() config := getUTLSTestConfig()
opensslCipherName := "TLS_CHACHA20_POLY1305_SHA256" opensslCipherName := "TLS_CHACHA20_POLY1305_SHA256"
test := &clientTest{ test := &clientTest{
name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), name: "UTLS-" + opensslCipherName + "-" + hello.helloName(),
args: []string{"-ciphersuites", opensslCipherName}, args: []string{"-ciphersuites", opensslCipherName},
config: config, config: config,
} }
runUTLSClientTestTLS13(t, test, helloID) runUTLSClientTestTLS13(t, test, hello)
} }
func testUTLSHandshakeClientECDHE_RSA_WITH_CHACHA20_POLY1305(t *testing.T, helloID ClientHelloID) { func testUTLSHandshakeClientECDHE_RSA_WITH_CHACHA20_POLY1305(t *testing.T, hello helloStrategy) {
config := getUTLSTestConfig() config := getUTLSTestConfig()
config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305} config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305}
opensslCipherName := "ECDHE-RSA-CHACHA20-POLY1305" opensslCipherName := "ECDHE-RSA-CHACHA20-POLY1305"
test := &clientTest{ test := &clientTest{
name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), name: "UTLS-" + opensslCipherName + "-" + hello.helloName(),
args: []string{"-cipher", opensslCipherName}, args: []string{"-cipher", opensslCipherName},
config: config, config: config,
} }
runUTLSClientTestTLS12(t, test, helloID) runUTLSClientTestTLS12(t, test, hello)
} }
func testUTLSHandshakeClientECDHE_ECDSA_WITH_CHACHA20_POLY1305(t *testing.T, helloID ClientHelloID) { func testUTLSHandshakeClientECDHE_ECDSA_WITH_CHACHA20_POLY1305(t *testing.T, hello helloStrategy) {
config := getUTLSTestConfig() config := getUTLSTestConfig()
config.CipherSuites = []uint16{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305} config.CipherSuites = []uint16{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305}
opensslCipherName := "ECDHE-ECDSA-CHACHA20-POLY1305" opensslCipherName := "ECDHE-ECDSA-CHACHA20-POLY1305"
test := &clientTest{ test := &clientTest{
name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), name: "UTLS-" + opensslCipherName + "-" + hello.helloName(),
args: []string{"-cipher", opensslCipherName}, args: []string{"-cipher", opensslCipherName},
config: config, config: config,
cert: testECDSACertificate, cert: testECDSACertificate,
key: testECDSAPrivateKey, key: testECDSAPrivateKey,
} }
runUTLSClientTestTLS12(t, test, helloID) runUTLSClientTestTLS12(t, test, hello)
} }
func runUTLSClientTestForVersion(t *testing.T, template *clientTest, prefix, option string, helloID ClientHelloID, omitSNI bool) { func runUTLSClientTestForVersion(t *testing.T, template *clientTest, prefix, option string, hello helloStrategy, omitSNI bool) {
test := *template test := *template
test.name = prefix + test.name test.name = prefix + test.name
if len(test.args) == 0 { if len(test.args) == 0 {
@ -387,18 +409,18 @@ func runUTLSClientTestForVersion(t *testing.T, template *clientTest, prefix, opt
} }
test.args = append([]string(nil), test.args...) test.args = append([]string(nil), test.args...)
test.args = append(test.args, option) test.args = append(test.args, option)
test.runUTLS(t, *update, helloID, omitSNI) test.runUTLS(t, *update, hello, omitSNI)
} }
func runUTLSClientTestTLS12(t *testing.T, template *clientTest, helloID ClientHelloID) { func runUTLSClientTestTLS12(t *testing.T, template *clientTest, hello helloStrategy) {
runUTLSClientTestForVersion(t, template, "TLSv12-", "-tls1_2", helloID, false) runUTLSClientTestForVersion(t, template, "TLSv12-", "-tls1_2", hello, false)
} }
func runUTLSClientTestTLS13(t *testing.T, template *clientTest, helloID ClientHelloID) { func runUTLSClientTestTLS13(t *testing.T, template *clientTest, hello helloStrategy) {
runUTLSClientTestForVersion(t, template, "TLSv13-", "-tls1_3", helloID, false) runUTLSClientTestForVersion(t, template, "TLSv13-", "-tls1_3", hello, false)
} }
func (test *clientTest) runUTLS(t *testing.T, write bool, helloID ClientHelloID, omitSNIExtension bool) { func (test *clientTest) runUTLS(t *testing.T, write bool, hello helloStrategy, omitSNIExtension bool) {
checkOpenSSLVersion(t) checkOpenSSLVersion(t)
var clientConn, serverConn net.Conn var clientConn, serverConn net.Conn
@ -423,7 +445,20 @@ func (test *clientTest) runUTLS(t *testing.T, write bool, helloID ClientHelloID,
t.Error("Explicit config is mandatory") t.Error("Explicit config is mandatory")
return return
} }
client := UClient(clientConn, config, helloID)
var client *UConn
switch h := hello.(type) {
case *helloID:
client = UClient(clientConn, config, h.id)
case *helloSpec:
client = UClient(clientConn, config, HelloCustom)
if err := client.ApplyPreset(h.spec); err != nil {
t.Errorf("got error: %v; expected to succeed", err)
return
}
default:
panic("unknown helloStrategy")
}
if omitSNIExtension { if omitSNIExtension {
if err := client.RemoveSNIExtension(); err != nil { if err := client.RemoveSNIExtension(); err != nil {

360
u_fingerprinter.go Normal file
View file

@ -0,0 +1,360 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tls
import (
"errors"
"fmt"
"strings"
"golang.org/x/crypto/cryptobyte"
)
// Fingerprinter is a struct largely for holding options for the FingerprintClientHello func
type Fingerprinter struct {
// KeepPSK will ensure that the PreSharedKey extension is passed along into the resulting ClientHelloSpec as-is
KeepPSK bool
// AllowBluntMimicry will ensure that unknown extensions are
// passed along into the resulting ClientHelloSpec as-is
// It will not ensure that the PSK is passed along, if you require that, use KeepPSK
// WARNING: there could be numerous subtle issues with ClientHelloSpecs
// that are generated with this flag which could compromise security and/or mimicry
AllowBluntMimicry bool
// AlwaysAddPadding will always add a UtlsPaddingExtension with BoringPaddingStyle
// at the end of the extensions list if it isn't found in the fingerprinted hello.
// This could be useful in scenarios where the hello you are fingerprinting does not
// have any padding, but you suspect that other changes you make to the final hello
// (including things like different SNI lengths) would cause padding to be necessary
AlwaysAddPadding bool
}
// FingerprintClientHello returns a ClientHelloSpec which is based on the
// ClientHello that is passed in as the data argument
//
// If the ClientHello passed in has extensions that are not recognized or cannot be handled
// it will return a non-nil error and a nil *ClientHelloSpec value
//
// The data should be the full tls record, including the record type/version/length header
// as well as the handshake type/length/version header
// https://tools.ietf.org/html/rfc5246#section-6.2
// https://tools.ietf.org/html/rfc5246#section-7.4
func (f *Fingerprinter) FingerprintClientHello(data []byte) (*ClientHelloSpec, error) {
clientHelloSpec := &ClientHelloSpec{}
s := cryptobyte.String(data)
var contentType uint8
var recordVersion uint16
if !s.ReadUint8(&contentType) || // record type
!s.ReadUint16(&recordVersion) || !s.Skip(2) { // record version and length
return nil, errors.New("unable to read record type, version, and length")
}
if recordType(contentType) != recordTypeHandshake {
return nil, errors.New("record is not a handshake")
}
var handshakeVersion uint16
var handshakeType uint8
if !s.ReadUint8(&handshakeType) || !s.Skip(3) || // message type and 3 byte length
!s.ReadUint16(&handshakeVersion) || !s.Skip(32) { // 32 byte random
return nil, errors.New("unable to read handshake message type, length, and random")
}
if handshakeType != typeClientHello {
return nil, errors.New("handshake message is not a ClientHello")
}
clientHelloSpec.TLSVersMin = recordVersion
clientHelloSpec.TLSVersMax = handshakeVersion
var ignoredSessionID cryptobyte.String
if !s.ReadUint8LengthPrefixed(&ignoredSessionID) {
return nil, errors.New("unable to read session id")
}
var cipherSuitesBytes cryptobyte.String
if !s.ReadUint16LengthPrefixed(&cipherSuitesBytes) {
return nil, errors.New("unable to read ciphersuites")
}
cipherSuites := []uint16{}
for !cipherSuitesBytes.Empty() {
var suite uint16
if !cipherSuitesBytes.ReadUint16(&suite) {
return nil, errors.New("unable to read ciphersuite")
}
cipherSuites = append(cipherSuites, unGREASEUint16(suite))
}
clientHelloSpec.CipherSuites = cipherSuites
if !readUint8LengthPrefixed(&s, &clientHelloSpec.CompressionMethods) {
return nil, errors.New("unable to read compression methods")
}
if s.Empty() {
// ClientHello is optionally followed by extension data
return clientHelloSpec, nil
}
var extensions cryptobyte.String
if !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() {
return nil, errors.New("unable to read extensions data")
}
for !extensions.Empty() {
var extension uint16
var extData cryptobyte.String
if !extensions.ReadUint16(&extension) ||
!extensions.ReadUint16LengthPrefixed(&extData) {
return nil, errors.New("unable to read extension data")
}
switch extension {
case extensionServerName:
// RFC 6066, Section 3
var nameList cryptobyte.String
if !extData.ReadUint16LengthPrefixed(&nameList) || nameList.Empty() {
return nil, errors.New("unable to read server name extension data")
}
var serverName string
for !nameList.Empty() {
var nameType uint8
var serverNameBytes cryptobyte.String
if !nameList.ReadUint8(&nameType) ||
!nameList.ReadUint16LengthPrefixed(&serverNameBytes) ||
serverNameBytes.Empty() {
return nil, errors.New("unable to read server name extension data")
}
if nameType != 0 {
continue
}
if len(serverName) != 0 {
return nil, errors.New("multiple names of the same name_type in server name extension are prohibited")
}
serverName = string(serverNameBytes)
if strings.HasSuffix(serverName, ".") {
return nil, errors.New("SNI value may not include a trailing dot")
}
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &SNIExtension{})
}
case extensionNextProtoNeg:
// draft-agl-tls-nextprotoneg-04
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &NPNExtension{})
case extensionStatusRequest:
// RFC 4366, Section 3.6
var statusType uint8
var ignored cryptobyte.String
if !extData.ReadUint8(&statusType) ||
!extData.ReadUint16LengthPrefixed(&ignored) ||
!extData.ReadUint16LengthPrefixed(&ignored) {
return nil, errors.New("unable to read status request extension data")
}
if statusType == statusTypeOCSP {
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &StatusRequestExtension{})
} else {
return nil, errors.New("status request extension statusType is not statusTypeOCSP")
}
case extensionSupportedCurves:
// RFC 4492, sections 5.1.1 and RFC 8446, Section 4.2.7
var curvesBytes cryptobyte.String
if !extData.ReadUint16LengthPrefixed(&curvesBytes) || curvesBytes.Empty() {
return nil, errors.New("unable to read supported curves extension data")
}
curves := []CurveID{}
for !curvesBytes.Empty() {
var curve uint16
if !curvesBytes.ReadUint16(&curve) {
return nil, errors.New("unable to read supported curves extension data")
}
curves = append(curves, CurveID(unGREASEUint16(curve)))
}
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &SupportedCurvesExtension{curves})
case extensionSupportedPoints:
// RFC 4492, Section 5.1.2
supportedPoints := []uint8{}
if !readUint8LengthPrefixed(&extData, &supportedPoints) ||
len(supportedPoints) == 0 {
return nil, errors.New("unable to read supported points extension data")
}
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &SupportedPointsExtension{supportedPoints})
case extensionSessionTicket:
// RFC 5077, Section 3.2
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &SessionTicketExtension{})
case extensionSignatureAlgorithms:
// RFC 5246, Section 7.4.1.4.1
var sigAndAlgs cryptobyte.String
if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() {
return nil, errors.New("unable to read signature algorithms extension data")
}
supportedSignatureAlgorithms := []SignatureScheme{}
for !sigAndAlgs.Empty() {
var sigAndAlg uint16
if !sigAndAlgs.ReadUint16(&sigAndAlg) {
return nil, errors.New("unable to read signature algorithms extension data")
}
supportedSignatureAlgorithms = append(
supportedSignatureAlgorithms, SignatureScheme(sigAndAlg))
}
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &SignatureAlgorithmsExtension{supportedSignatureAlgorithms})
case extensionSignatureAlgorithmsCert:
// RFC 8446, Section 4.2.3
if f.AllowBluntMimicry {
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &GenericExtension{extension, extData})
} else {
return nil, errors.New("unsupported extension SignatureAlgorithmsCert")
}
case extensionRenegotiationInfo:
// RFC 5746, Section 3.2
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &RenegotiationInfoExtension{RenegotiateOnceAsClient})
case extensionALPN:
// RFC 7301, Section 3.1
var protoList cryptobyte.String
if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() {
return nil, errors.New("unable to read ALPN extension data")
}
alpnProtocols := []string{}
for !protoList.Empty() {
var proto cryptobyte.String
if !protoList.ReadUint8LengthPrefixed(&proto) || proto.Empty() {
return nil, errors.New("unable to read ALPN extension data")
}
alpnProtocols = append(alpnProtocols, string(proto))
}
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &ALPNExtension{alpnProtocols})
case extensionSCT:
// RFC 6962, Section 3.3.1
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &SCTExtension{})
case extensionSupportedVersions:
// RFC 8446, Section 4.2.1
var versList cryptobyte.String
if !extData.ReadUint8LengthPrefixed(&versList) || versList.Empty() {
return nil, errors.New("unable to read supported versions extension data")
}
supportedVersions := []uint16{}
for !versList.Empty() {
var vers uint16
if !versList.ReadUint16(&vers) {
return nil, errors.New("unable to read supported versions extension data")
}
supportedVersions = append(supportedVersions, unGREASEUint16(vers))
}
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &SupportedVersionsExtension{supportedVersions})
// If SupportedVersionsExtension is present, use that instead of record+handshake versions
clientHelloSpec.TLSVersMin = 0
clientHelloSpec.TLSVersMax = 0
case extensionKeyShare:
// RFC 8446, Section 4.2.8
var clientShares cryptobyte.String
if !extData.ReadUint16LengthPrefixed(&clientShares) {
return nil, errors.New("unable to read key share extension data")
}
keyShares := []KeyShare{}
for !clientShares.Empty() {
var ks KeyShare
var group uint16
if !clientShares.ReadUint16(&group) ||
!readUint16LengthPrefixed(&clientShares, &ks.Data) ||
len(ks.Data) == 0 {
return nil, errors.New("unable to read key share extension data")
}
ks.Group = CurveID(unGREASEUint16(group))
// if not GREASE, key share data will be discarded as it should
// be generated per connection
if ks.Group != GREASE_PLACEHOLDER {
ks.Data = nil
}
keyShares = append(keyShares, ks)
}
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &KeyShareExtension{keyShares})
case extensionPSKModes:
// RFC 8446, Section 4.2.9
// TODO: PSK Modes have their own form of GREASE-ing which is not currently implemented
// the current functionality will NOT re-GREASE/re-randomize these values when using a fingerprinted spec
// https://github.com/refraction-networking/utls/pull/58#discussion_r522354105
// https://tools.ietf.org/html/draft-ietf-tls-grease-01#section-2
pskModes := []uint8{}
if !readUint8LengthPrefixed(&extData, &pskModes) {
return nil, errors.New("unable to read PSK extension data")
}
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &PSKKeyExchangeModesExtension{pskModes})
case utlsExtensionExtendedMasterSecret:
// https://tools.ietf.org/html/rfc7627
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &UtlsExtendedMasterSecretExtension{})
case utlsExtensionPadding:
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle})
case fakeExtensionChannelID, fakeCertCompressionAlgs, fakeRecordSizeLimit:
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &GenericExtension{extension, extData})
case extensionPreSharedKey:
// RFC 8446, Section 4.2.11
if f.KeepPSK {
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &GenericExtension{extension, extData})
} else {
return nil, errors.New("unsupported extension PreSharedKey")
}
case extensionCookie:
// RFC 8446, Section 4.2.2
if f.AllowBluntMimicry {
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &GenericExtension{extension, extData})
} else {
return nil, errors.New("unsupported extension Cookie")
}
case extensionEarlyData:
// RFC 8446, Section 4.2.10
if f.AllowBluntMimicry {
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &GenericExtension{extension, extData})
} else {
return nil, errors.New("unsupported extension EarlyData")
}
default:
if isGREASEUint16(extension) {
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &UtlsGREASEExtension{unGREASEUint16(extension), extData})
} else if f.AllowBluntMimicry {
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &GenericExtension{extension, extData})
} else {
return nil, fmt.Errorf("unsupported extension %#x", extension)
}
continue
}
}
if f.AlwaysAddPadding {
alreadyHasPadding := false
for _, ext := range clientHelloSpec.Extensions {
if _, ok := ext.(*UtlsPaddingExtension); ok {
alreadyHasPadding = true
break
}
}
if !alreadyHasPadding {
clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle})
}
}
return clientHelloSpec, nil
}

716
u_fingerprinter_test.go Normal file
View file

@ -0,0 +1,716 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tls
import (
"bytes"
"encoding/hex"
"fmt"
"io/ioutil"
"net"
"reflect"
"testing"
)
func assertEquality(t *testing.T, fieldName string, expected, actual interface{}) {
if kActual, ok := actual.(KeyShare); ok {
kExpected := expected.(KeyShare)
assertEquality(t, fieldName, kExpected.Group, kActual.Group)
return
}
if fieldName == "SupportedCurves" || fieldName == "KeyShares" {
cExpected := expected.(CurveID)
cActual := actual.(CurveID)
if isGREASEUint16(uint16(cExpected)) && isGREASEUint16(uint16(cActual)) {
return
}
}
if fieldName == "SupportedVersions" || fieldName == "CipherSuites" {
cExpected := expected.(uint16)
cActual := actual.(uint16)
if isGREASEUint16(cExpected) && isGREASEUint16(cActual) {
return
}
}
if expected != actual {
t.Errorf("%v fields not equal, expected: %v, got: %v", fieldName, expected, actual)
}
}
func compareClientHelloFields(t *testing.T, fieldName string, expected, actual *ClientHelloMsg) {
rExpected := reflect.ValueOf(expected)
if rExpected.Kind() != reflect.Ptr || rExpected.Elem().Kind() != reflect.Struct {
t.Errorf("Error using reflect to compare Hello fields")
}
rActual := reflect.ValueOf(actual)
if rActual.Kind() != reflect.Ptr || rActual.Elem().Kind() != reflect.Struct {
t.Errorf("Error using reflect to compare Hello fields")
}
rExpected = rExpected.Elem()
rActual = rActual.Elem()
fExpected := rExpected.FieldByName(fieldName)
fActual := rActual.FieldByName(fieldName)
if !(fExpected.IsValid() && fActual.IsValid()) {
t.Errorf("Error using reflect to lookup Hello field name: %v", fieldName)
}
if fExpected.Kind() == reflect.Slice {
sExpected := fExpected.Slice(0, fExpected.Len())
sActual := fActual.Slice(0, fActual.Len())
if sExpected.Len() != sActual.Len() {
t.Errorf("%v fields slice length not equal, expected: %v, got: %v", fieldName, fExpected, fActual)
}
for i := 0; i < sExpected.Len(); i++ {
assertEquality(t, fieldName, sExpected.Index(i).Interface(), sActual.Index(i).Interface())
}
} else {
assertEquality(t, fieldName, fExpected.Interface(), fActual.Interface())
}
}
func checkUTLSExtensionsEquality(t *testing.T, expected, actual TLSExtension) {
if expectedGrease, ok := expected.(*UtlsGREASEExtension); ok {
if actualGrease, ok := actual.(*UtlsGREASEExtension); ok {
if bytes.Equal(expectedGrease.Body, actualGrease.Body) {
return
}
}
}
if expected.Len() != actual.Len() {
t.Errorf("extension types length not equal\nexpected: %#v\ngot: %#v", expected, actual)
}
actualBytes, err := ioutil.ReadAll(actual)
if err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
expectedBytes, err := ioutil.ReadAll(expected)
if err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
logInequality := func() {
t.Errorf("extensions not equal\nexpected: %#v\nbytes:%#x\ngot: %#v\nbytes: %#x", expected, expectedBytes, actual, actualBytes)
}
if !bytes.Equal(expectedBytes, actualBytes) {
// handle all the cases where GREASE or other factors can cause byte unalignment
// at this point concrete types must match
expectedType := reflect.TypeOf(expected)
actualType := reflect.TypeOf(actual)
if expectedType != actualType {
t.Errorf("extensions not equal\nexpected: %#v\nbytes:%#x\ngot: %#v\nbytes: %#x", expected, expectedBytes, actual, actualBytes)
return
}
switch expectedExtension := expected.(type) {
case *SupportedCurvesExtension:
actualExtension := expected.(*SupportedCurvesExtension)
for i, expectedCurve := range expectedExtension.Curves {
actualCurve := actualExtension.Curves[i]
if expectedCurve == actualCurve {
continue
}
if isGREASEUint16(uint16(expectedCurve)) && isGREASEUint16(uint16(actualCurve)) {
continue
}
logInequality()
return
}
case *KeyShareExtension:
actualExtension := expected.(*KeyShareExtension)
for i, expectedKeyShare := range expectedExtension.KeyShares {
actualKeyShare := actualExtension.KeyShares[i]
// KeyShare data is unique per connection
if actualKeyShare.Group == expectedKeyShare.Group {
continue
}
if isGREASEUint16(uint16(expectedKeyShare.Group)) && isGREASEUint16(uint16(actualKeyShare.Group)) {
continue
}
logInequality()
return
}
case *SupportedVersionsExtension:
actualExtension := expected.(*SupportedVersionsExtension)
for i, expectedVersion := range expectedExtension.Versions {
actualVersion := actualExtension.Versions[i]
if isGREASEUint16(expectedVersion) && isGREASEUint16(actualVersion) || actualVersion == expectedVersion {
continue
}
logInequality()
return
}
default:
logInequality()
return
}
}
}
// Conn.vers is sometimes left to zero which is unacceptable to uTLS' SetTLSVers
// https://github.com/refraction-networking/utls/blob/f7e7360167ed2903ef12898634512b66f8c3aad0/u_conn.go#L564-L566
// https://github.com/refraction-networking/utls/blob/f7e7360167ed2903ef12898634512b66f8c3aad0/conn.go#L945-L948
func createMinTLSVersion(vers uint16) uint16 {
if vers == 0 {
return VersionTLS10
}
return vers
}
// prependRecordHeader prepends a record header to a handshake messsage
// if attempting to mimic an existing connection the minTLSVersion can be found
// in the Conn.vers field
func prependRecordHeader(hello []byte, minTLSVersion uint16) []byte {
l := len(hello)
if minTLSVersion == 0 {
minTLSVersion = VersionTLS10
}
header := []byte{
uint8(recordTypeHandshake), // type
uint8(minTLSVersion >> 8 & 0xff), uint8(minTLSVersion & 0xff), // record version is the minimum supported
uint8(l >> 8 & 0xff), uint8(l & 0xff), // length
}
return append(header, hello...)
}
func checkUTLSFingerPrintClientHello(t *testing.T, clientHelloID ClientHelloID, serverName string) {
uconn := UClient(&net.TCPConn{}, &Config{ServerName: serverName}, clientHelloID)
if err := uconn.BuildHandshakeState(); err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
generatedUConn := UClient(&net.TCPConn{}, &Config{ServerName: "foobar"}, HelloCustom)
fingerprinter := &Fingerprinter{}
minTLSVers := createMinTLSVersion(uconn.vers)
generatedSpec, err := fingerprinter.FingerprintClientHello(prependRecordHeader(uconn.HandshakeState.Hello.Raw, minTLSVers))
if err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
if err := generatedUConn.ApplyPreset(generatedSpec); err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
if err := generatedUConn.BuildHandshakeState(); err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
if len(uconn.HandshakeState.Hello.Raw) != len(generatedUConn.HandshakeState.Hello.Raw) {
t.Errorf("UConn from fingerprint has %d length, should have %d", len(generatedUConn.HandshakeState.Hello.Raw), len(uconn.HandshakeState.Hello.Raw))
}
// We can't effectively check the extensions on randomized client hello ids
if !(clientHelloID == HelloRandomized || clientHelloID == HelloRandomizedALPN || clientHelloID == HelloRandomizedNoALPN) {
for i, originalExtension := range uconn.Extensions {
if _, ok := originalExtension.(*UtlsPaddingExtension); ok {
// We can't really compare padding extensions in this way
continue
}
generatedExtension := generatedUConn.Extensions[i]
checkUTLSExtensionsEquality(t, originalExtension, generatedExtension)
}
}
fieldsToTest := []string{
"Vers", "CipherSuites", "CompressionMethods", "NextProtoNeg", "ServerName", "OcspStapling", "Scts", "SupportedCurves",
"SupportedPoints", "TicketSupported", "SupportedSignatureAlgorithms", "SecureRenegotiation", "SecureRenegotiationSupported", "AlpnProtocols",
"SupportedSignatureAlgorithmsCert", "SupportedVersions", "KeyShares", "EarlyData", "PskModes", "PskIdentities", "PskBinders",
}
for _, field := range fieldsToTest {
compareClientHelloFields(t, field, uconn.HandshakeState.Hello, generatedUConn.HandshakeState.Hello)
}
}
func TestUTLSFingerprintClientHello(t *testing.T) {
clientHellosToTest := []ClientHelloID{
HelloChrome_58, HelloChrome_70, HelloChrome_83, HelloFirefox_55, HelloFirefox_63, HelloIOS_11_1, HelloIOS_12_1, HelloRandomized, HelloRandomizedALPN, HelloRandomizedNoALPN}
serverNames := []string{"foobar"}
for _, clientHello := range clientHellosToTest {
for _, serverName := range serverNames {
t.Logf("checking fingerprint generated client hello spec against %v and server name: %v", clientHello, serverName)
checkUTLSFingerPrintClientHello(t, clientHello, "foobar")
}
}
}
func TestUTLSFingerprintClientHelloBluntMimicry(t *testing.T) {
serverName := "foobar"
var extensionId uint16 = 0xfeed
extensionData := []byte("random data")
specWithGeneric, err := utlsIdToSpec(HelloChrome_Auto)
if err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
specWithGeneric.Extensions = append(specWithGeneric.Extensions, &GenericExtension{extensionId, extensionData})
uconn := UClient(&net.TCPConn{}, &Config{ServerName: serverName}, HelloCustom)
if err := uconn.ApplyPreset(&specWithGeneric); err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
if err := uconn.BuildHandshakeState(); err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
f := &Fingerprinter{}
minTLSVers := createMinTLSVersion(uconn.vers)
_, err = f.FingerprintClientHello(prependRecordHeader(uconn.HandshakeState.Hello.Raw, minTLSVers))
if err == nil {
t.Errorf("expected error generating spec from client hello with GenericExtension")
}
f = &Fingerprinter{AllowBluntMimicry: true}
generatedSpec, err := f.FingerprintClientHello(prependRecordHeader(uconn.HandshakeState.Hello.Raw, minTLSVers))
if err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
for _, ext := range generatedSpec.Extensions {
if genericExtension, ok := (ext).(*GenericExtension); ok {
if genericExtension.Id == extensionId && bytes.Equal(genericExtension.Data, extensionData) {
return
}
}
}
t.Errorf("generated ClientHelloSpec with BluntMimicry did not correctly carry over generic extension")
}
func TestUTLSFingerprintClientHelloAlwaysAddPadding(t *testing.T) {
serverName := "foobar"
specWithoutPadding, err := utlsIdToSpec(HelloIOS_12_1)
if err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
specWithPadding, err := utlsIdToSpec(HelloChrome_83)
if err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
uconnWithoutPadding := UClient(&net.TCPConn{}, &Config{ServerName: serverName}, HelloCustom)
uconnWithPadding := UClient(&net.TCPConn{}, &Config{ServerName: serverName}, HelloCustom)
if err := uconnWithoutPadding.ApplyPreset(&specWithoutPadding); err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
if err := uconnWithoutPadding.BuildHandshakeState(); err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
if err := uconnWithPadding.ApplyPreset(&specWithPadding); err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
if err := uconnWithPadding.BuildHandshakeState(); err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
f := &Fingerprinter{}
minTLSVersWithoutPadding := createMinTLSVersion(uconnWithoutPadding.vers)
fingerprintedWithoutPadding, err := f.FingerprintClientHello(prependRecordHeader(uconnWithoutPadding.HandshakeState.Hello.Raw, minTLSVersWithoutPadding))
if err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
for _, ext := range fingerprintedWithoutPadding.Extensions {
if _, ok := ext.(*UtlsPaddingExtension); ok {
t.Errorf("padding extension should not be present on fingerprinted ClientHelloSpec without AlwaysAddPadding set")
break
}
}
f = &Fingerprinter{AlwaysAddPadding: true}
generatedSpec, err := f.FingerprintClientHello(prependRecordHeader(uconnWithoutPadding.HandshakeState.Hello.Raw, minTLSVersWithoutPadding))
if err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
hasPadding := false
for _, ext := range generatedSpec.Extensions {
if _, ok := ext.(*UtlsPaddingExtension); ok {
hasPadding = true
break
}
}
if !hasPadding {
t.Errorf("expected padding extension on fingerprinted ClientHelloSpec with AlwaysAddPadding set")
}
f = &Fingerprinter{AlwaysAddPadding: true}
minTLSVersWithPadding := createMinTLSVersion(uconnWithPadding.vers)
generatedSpec, err = f.FingerprintClientHello(prependRecordHeader(uconnWithPadding.HandshakeState.Hello.Raw, minTLSVersWithPadding))
if err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
hasPadding = false
for _, ext := range generatedSpec.Extensions {
if _, ok := ext.(*UtlsPaddingExtension); ok {
if hasPadding {
t.Errorf("found double padding extension on fingerprinted ClientHelloSpec with AlwaysAddPadding set")
}
hasPadding = true
}
}
if !hasPadding {
t.Errorf("expected padding extension on fingerprinted ClientHelloSpec with AlwaysAddPadding set")
}
}
func TestUTLSFingerprintClientHelloKeepPSK(t *testing.T) {
// TLSv1.3 Record Layer: Handshake Protocol: Client Hello
// Content Type: Handshake (22)
// Version: TLS 1.0 (0x0301)
// Length: 576
// Handshake Protocol: Client Hello
// Handshake Type: Client Hello (1)
// Length: 572
// Version: TLS 1.2 (0x0303)
// Random: 5cef5aa9122008e37f0f74d717cd4ae0f745daba4292e6fb…
// Session ID Length: 32
// Session ID: 8c4aa23444084eeb70097efe0b8f6e3a56c717abd67505c9…
// Cipher Suites Length: 32
// Cipher Suites (16 suites)
// Compression Methods Length: 1
// Compression Methods (1 method)
// Extensions Length: 467
// Extension: Reserved (GREASE) (len=0)
// Type: Reserved (GREASE) (14906)
// Length: 0
// Data: <MISSING>
// Extension: server_name (len=22)
// Type: server_name (0)
// Length: 22
// Server Name Indication extension
// Server Name list length: 20
// Server Name Type: host_name (0)
// Server Name length: 17
// Server Name: edgeapi.slack.com
// Extension: extended_master_secret (len=0)
// Type: extended_master_secret (23)
// Length: 0
// Extension: renegotiation_info (len=1)
// Type: renegotiation_info (65281)
// Length: 1
// Renegotiation Info extension
// Renegotiation info extension length: 0
// Extension: supported_groups (len=10)
// Type: supported_groups (10)
// Length: 10
// Supported Groups List Length: 8
// Supported Groups (4 groups)
// Supported Group: Reserved (GREASE) (0xdada)
// Supported Group: x25519 (0x001d)
// Supported Group: secp256r1 (0x0017)
// Supported Group: secp384r1 (0x0018)
// Extension: ec_point_formats (len=2)
// Type: ec_point_formats (11)
// Length: 2
// EC point formats Length: 1
// Elliptic curves point formats (1)
// Extension: session_ticket (len=0)
// Type: session_ticket (35)
// Length: 0
// Data (0 bytes)
// Extension: application_layer_protocol_negotiation (len=14)
// Type: application_layer_protocol_negotiation (16)
// Length: 14
// ALPN Extension Length: 12
// ALPN Protocol
// ALPN string length: 2
// ALPN Next Protocol: h2
// ALPN string length: 8
// ALPN Next Protocol: http/1.1
// Extension: status_request (len=5)
// Type: status_request (5)
// Length: 5
// Certificate Status Type: OCSP (1)
// Responder ID list Length: 0
// Request Extensions Length: 0
// Extension: signature_algorithms (len=18)
// Type: signature_algorithms (13)
// Length: 18
// Signature Hash Algorithms Length: 16
// Signature Hash Algorithms (8 algorithms)
// Extension: signed_certificate_timestamp (len=0)
// Type: signed_certificate_timestamp (18)
// Length: 0
// Extension: key_share (len=43)
// Type: key_share (51)
// Length: 43
// Key Share extension
// Client Key Share Length: 41
// Key Share Entry: Group: Reserved (GREASE), Key Exchange length: 1
// Group: Reserved (GREASE) (56026)
// Key Exchange Length: 1
// Key Exchange: 00
// Key Share Entry: Group: x25519, Key Exchange length: 32
// Group: x25519 (29)
// Key Exchange Length: 32
// Key Exchange: e35e636d4e2dcd5f39309170285dab92dbe81fefe4926826…
// Extension: psk_key_exchange_modes (len=2)
// Type: psk_key_exchange_modes (45)
// Length: 2
// PSK Key Exchange Modes Length: 1
// PSK Key Exchange Mode: PSK with (EC)DHE key establishment (psk_dhe_ke) (1)
// Extension: supported_versions (len=11)
// Type: supported_versions (43)
// Length: 11
// Supported Versions length: 10
// Supported Version: Unknown (0x2a2a)
// Supported Version: TLS 1.3 (0x0304)
// Supported Version: TLS 1.2 (0x0303)
// Supported Version: TLS 1.1 (0x0302)
// Supported Version: TLS 1.0 (0x0301)
// Extension: compress_certificate (len=3)
// Type: compress_certificate (27)
// Length: 3
// Algorithms Length: 2
// Algorithm: brotli (2)
// Extension: Reserved (GREASE) (len=1)
// Type: Reserved (GREASE) (19018)
// Length: 1
// Data: 00
// Extension: pre_shared_key (len=267)
// Type: pre_shared_key (41)
// Length: 267
// Pre-Shared Key extension
byteString := []byte("16030102400100023c03035cef5aa9122008e37f0f74d717cd4ae0f745daba4292e6fbca3cd5bf9123498f208c4aa23444084eeb70097efe0b8f6e3a56c717abd67505c950aab314de59bd8f00204a4a130113021303c02bc02fc02cc030cca9cca8c013c014009c009d002f0035010001d33a3a0000000000160014000011656467656170692e736c61636b2e636f6d00170000ff01000100000a000a0008dada001d00170018000b00020100002300000010000e000c02683208687474702f312e31000500050100000000000d0012001004030804040105030805050108060601001200000033002b0029dada000100001d0020e35e636d4e2dcd5f39309170285dab92dbe81fefe4926826cec1ef881321687e002d00020101002b000b0a2a2a0304030303020301001b00030200024a4a0001000029010b00e600e017fab59672c1966ae78fc4dacd7efb42e735de956e3f96d342bb8e63a5233ce21c92d6d75036601d74ccbc3ca0085f3ac2ebbd83da13501ac3c6d612bcb453fb206a39a8112d768bea1976d7c14e6de9aa0ee70ea732554d3c57d1a993f1044a46c1fb371811039ef30582cacf41bd497121d67793b8ee4df7a60d525f7df052fd66cda7f141bb553d9253816752d923ac7c71426179db4f26a7d42f0d65a2dd2dbaafb86fa17b2da23fd57c5064c76551cfda86304051231e4da9e697fedbcb5ae8cb2f6cb92f71164acf2edff5bccc1266cd648a53cc46262eabf40727bcb6958a3d1300212083e99d791672d39919dcb387f2fa7aeee938ec32ecf4b861306f7df4f9a8a746")
helloBytes := make([]byte, hex.DecodedLen(len(byteString)))
_, err := hex.Decode(helloBytes, byteString)
if err != nil {
t.Errorf("got error: %v; expected to succeed", err)
return
}
f := &Fingerprinter{}
_, err = f.FingerprintClientHello(helloBytes)
if err == nil {
t.Errorf("expected error generating spec from client hello with PSK")
}
f = &Fingerprinter{KeepPSK: true}
generatedSpec, err := f.FingerprintClientHello(helloBytes)
if err != nil {
t.Errorf("got error: %v; expected to succeed", err)
return
}
for _, ext := range generatedSpec.Extensions {
if genericExtension, ok := (ext).(*GenericExtension); ok {
if genericExtension.Id == extensionPreSharedKey {
return
}
}
}
t.Errorf("generated ClientHelloSpec with KeepPSK does not include preshared key extension")
}
func TestUTLSHandshakeClientFingerprintedSpecFromChrome_58(t *testing.T) {
helloID := HelloChrome_58
serverName := "foobar"
originalConfig := getUTLSTestConfig()
originalConfig.ServerName = serverName
uconn := UClient(&net.TCPConn{}, originalConfig, helloID)
if err := uconn.BuildHandshakeState(); err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
f := &Fingerprinter{}
minTLSVers := createMinTLSVersion(uconn.vers)
generatedSpec, err := f.FingerprintClientHello(prependRecordHeader(uconn.HandshakeState.Hello.Raw, minTLSVers))
if err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
hello := &helloSpec{
name: fmt.Sprintf("%v-fingerprinted", helloID.Str()),
spec: generatedSpec,
}
newConfig := getUTLSTestConfig()
newConfig.ServerName = serverName
opensslCipherName := "ECDHE-RSA-AES128-GCM-SHA256"
test := &clientTest{
name: "UTLS-" + opensslCipherName + "-" + hello.helloName(),
args: []string{"-cipher", opensslCipherName},
config: newConfig,
}
runUTLSClientTestTLS12(t, test, hello)
}
func TestUTLSHandshakeClientFingerprintedSpecFromChrome_70(t *testing.T) {
helloID := HelloChrome_70
serverName := "foobar"
originalConfig := getUTLSTestConfig()
originalConfig.ServerName = serverName
uconn := UClient(&net.TCPConn{}, originalConfig, helloID)
if err := uconn.BuildHandshakeState(); err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
f := &Fingerprinter{}
minTLSVers := createMinTLSVersion(uconn.vers)
generatedSpec, err := f.FingerprintClientHello(prependRecordHeader(uconn.HandshakeState.Hello.Raw, minTLSVers))
if err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
hello := &helloSpec{
name: fmt.Sprintf("%v-fingerprinted", helloID.Str()),
spec: generatedSpec,
}
newConfig := getUTLSTestConfig()
newConfig.ServerName = serverName
opensslCipherName := "TLS_AES_128_GCM_SHA256"
test := &clientTest{
name: "UTLS-" + opensslCipherName + "-" + hello.helloName(),
args: []string{"-ciphersuites", opensslCipherName},
config: newConfig,
}
runUTLSClientTestTLS13(t, test, hello)
}
func TestUTLSHandshakeClientFingerprintedSpecFromRaw(t *testing.T) {
// TLSv1.3 Record Layer: Handshake Protocol: Client Hello
// Content Type: Handshake (22)
// Version: TLS 1.0 (0x0301)
// Length: 512
// Handshake Protocol: Client Hello
// Handshake Type: Client Hello (1)
// Length: 508
// Version: TLS 1.2 (0x0303)
// Random: 7fd76fa530c24816ea9e4a6cf2e939f2350b9486a7bac58e…
// Session ID Length: 32
// Session ID: d9b01fc4f4b6fe14fe9ce652442d66588d982cb25913d866…
// Cipher Suites Length: 36
// Cipher Suites (18 suites)
// Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
// Cipher Suite: TLS_CHACHA20_POLY1305_SHA256 (0x1303)
// Cipher Suite: TLS_AES_256_GCM_SHA384 (0x1302)
// Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)
// Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
// Cipher Suite: TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca9)
// Cipher Suite: TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8)
// Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)
// Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
// Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a)
// Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009)
// Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)
// Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)
// Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c)
// Cipher Suite: TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009d)
// Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)
// Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035)
// Cipher Suite: TLS_RSA_WITH_3DES_EDE_CBC_SHA (0x000a)
// Compression Methods Length: 1
// Compression Methods (1 method)
// Extensions Length: 399
// Extension: server_name (len=34)
// Type: server_name (0)
// Length: 34
// Server Name Indication extension
// Extension: extended_master_secret (len=0)
// Type: extended_master_secret (23)
// Length: 0
// Extension: renegotiation_info (len=1)
// Type: renegotiation_info (65281)
// Length: 1
// Renegotiation Info extension
// Extension: supported_groups (len=14)
// Type: supported_groups (10)
// Length: 14
// Supported Groups List Length: 12
// Supported Groups (6 groups)
// Extension: ec_point_formats (len=2)
// Type: ec_point_formats (11)
// Length: 2
// EC point formats Length: 1
// Elliptic curves point formats (1)
// Extension: application_layer_protocol_negotiation (len=14)
// Type: application_layer_protocol_negotiation (16)
// Length: 14
// ALPN Extension Length: 12
// ALPN Protocol
// Extension: status_request (len=5)
// Type: status_request (5)
// Length: 5
// Certificate Status Type: OCSP (1)
// Responder ID list Length: 0
// Request Extensions Length: 0
// Extension: key_share (len=107)
// Type: key_share (51)
// Length: 107
// Key Share extension
// Extension: supported_versions (len=5)
// Type: supported_versions (43)
// Length: 5
// Supported Versions length: 4
// Supported Version: TLS 1.3 (0x0304)
// Supported Version: TLS 1.2 (0x0303)
// Extension: signature_algorithms (len=24)
// Type: signature_algorithms (13)
// Length: 24
// Signature Hash Algorithms Length: 22
// Signature Hash Algorithms (11 algorithms)
// Extension: record_size_limit (len=2)
// Type: record_size_limit (28)
// Length: 2
// Record Size Limit: 16385
// Extension: padding (len=143)
// Type: padding (21)
// Length: 143
// Padding Data: 000000000000000000000000000000000000000000000000…
byteString := []byte("1603010200010001fc03037fd76fa530c24816ea9e4a6cf2e939f2350b9486a7bac58ece5753767fb6112420d9b01fc4f4b6fe14fe9ce652442d66588d982cb25913d866348bde54d3899abe0024130113031302c02bc02fcca9cca8c02cc030c00ac009c013c014009c009d002f0035000a0100018f00000022002000001d70656f706c652d70612e636c69656e7473362e676f6f676c652e636f6d00170000ff01000100000a000e000c001d00170018001901000101000b000201000010000e000c02683208687474702f312e310005000501000000000033006b0069001d002065e566ff33dfbeb012e3b13b87d75612bd0fbc3963673df90afed533dccc9b5400170041047fcc2666d04c31272a2e39905c771a89edf5a71dae301ec2fa0e7bc4d0e06580a0d36324e3dc4f29e200a8905badd11c00daf11588977bf501597dac5fdc55bf002b00050403040303000d0018001604030503060308040805080604010501060102030201001c000240010015008f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
helloBytes := make([]byte, hex.DecodedLen(len(byteString)))
_, err := hex.Decode(helloBytes, byteString)
if err != nil {
t.Errorf("got error: %v; expected to succeed", err)
return
}
f := &Fingerprinter{}
generatedSpec, err := f.FingerprintClientHello(helloBytes)
if err != nil {
t.Errorf("got error: %v; expected to succeed", err)
}
hello := &helloSpec{
name: "raw-capture-fingerprinted",
spec: generatedSpec,
}
config := getUTLSTestConfig()
opensslCipherName := "TLS_AES_128_GCM_SHA256"
test := &clientTest{
name: "UTLS-" + opensslCipherName + "-" + hello.helloName(),
args: []string{"-ciphersuites", opensslCipherName},
config: config,
}
runUTLSClientTestTLS13(t, test, hello)
}

View file

@ -617,6 +617,9 @@ func (uconn *UConn) ApplyPreset(p *ClientHelloSpec) error {
uconn.Extensions = make([]TLSExtension, len(p.Extensions)) uconn.Extensions = make([]TLSExtension, len(p.Extensions))
copy(uconn.Extensions, p.Extensions) copy(uconn.Extensions, p.Extensions)
// Check whether NPN extension actually exists
var haveNPN bool
// reGrease, and point things to each other // reGrease, and point things to each other
for _, e := range uconn.Extensions { for _, e := range uconn.Extensions {
switch ext := e.(type) { switch ext := e.(type) {
@ -681,8 +684,15 @@ func (uconn *UConn) ApplyPreset(p *ClientHelloSpec) error {
ext.Versions[i] = GetBoringGREASEValue(uconn.greaseSeed, ssl_grease_version) ext.Versions[i] = GetBoringGREASEValue(uconn.greaseSeed, ssl_grease_version)
} }
} }
case *NPNExtension:
haveNPN = true
} }
} }
// The default golang behavior in makeClientHello always sets NextProtoNeg if NextProtos is set,
// but NextProtos is also used by ALPN and our spec nmay not actually have a NPN extension
hello.NextProtoNeg = haveNPN
return nil return nil
} }