rust_jarm 0.3.9

JARM fingerprinting in native Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
#[cfg(test)]
mod tests {
    use rstest::rstest;

    use rust_jarm::{cipher_bytes, CipherList, CipherOrder, Jarm, JarmPart, JarmRng, PacketSpecification, TestRng, TlsVersion, TlsVersionSupport, version_byte};

    fn test_rng() -> TestRng {
        TestRng {}
    }

    #[test]
    fn test_jarm_hash() {
        let expected_hash = "27d27d27d27d27d27d27d27d27d27debd865e63a4441da99411bab3aadfedf".to_string();

        let mut jarm = Jarm::default();
        jarm.parts = vec![
            JarmPart::new("c02b|0303|h2|0000-0017-ff01-000b-0023-0010"),
            JarmPart::new("c02b|0303|h2|0000-0017-ff01-000b-0023-0010"),
            JarmPart::new("c02b|0303|h2|0000-0017-ff01-000b-0023-0010"),
            JarmPart::new("c02b|0303|h2|0000-0017-ff01-000b-0023-0010"),
            JarmPart::new("c02b|0303|h2|0000-0017-ff01-000b-0023-0010"),
            JarmPart::new("c02b|0303|h2|0000-0017-ff01-000b-0023-0010"),
            JarmPart::new("c02b|0303|h2|0000-0017-ff01-000b-0023-0010"),
            JarmPart::new("c02b|0303|h2|0000-0017-ff01-000b-0023-0010"),
            JarmPart::new("c02b|0303|h2|0000-0017-ff01-000b-0023-0010"),
            JarmPart::new("c02b|0303|h2|0000-0017-ff01-000b-0023-0010"),
        ];
        jarm.rng = Box::new(TestRng {});  // use the mock rng

        assert_eq!(jarm.hash().unwrap(), expected_hash);
    }

    #[rstest]
    #[case("0004", "01")]
    #[case("c027", "25")]
    #[case("c02b", "27")]
    #[case("1305", "45")]
    fn test_cipher_bytes(#[case] input: &str, #[case] expected: &str) {
        assert_eq!(cipher_bytes(input), expected);
    }

    #[test]
    fn test_cipher_bytes_empty_string() {
        assert_eq!(cipher_bytes(""), "00");
    }

    #[rstest]
    #[case("0000")]
    #[case("0034")]
    fn test_cipher_bytes_is_unexpected(#[case] input: &str) {
        let result = cipher_bytes(input);
        assert_eq!(result, "46");
    }

    #[test]
    fn test_version_byte() {
        assert_eq!(version_byte(""), '0');
        assert_eq!(version_byte("0301"), 'b');
        assert_eq!(version_byte("0303"), 'd');
        assert_eq!(version_byte("0304"), 'e');
    }

    #[test]
    fn test_jarm_hash_null() {
        let expected_hash = String::from_utf8(vec![b'0'; 62]).unwrap();

        let mut jarm = Jarm::default();
        jarm.parts = vec![
            JarmPart::new("|||"),
            JarmPart::new("|||"),
            JarmPart::new("|||"),
            JarmPart::new("|||"),
            JarmPart::new("|||"),
            JarmPart::new("|||"),
            JarmPart::new("|||"),
            JarmPart::new("|||"),
            JarmPart::new("|||"),
            JarmPart::new("|||"),
        ];

        assert_eq!(jarm.hash().unwrap(), expected_hash);
    }

    #[test]
    fn test_build_tls_1_2() {
        // let expected_packet = b"\x16\x03\x03\x01\xb5\x01\x00\x01\xb1\x03\x03\xf4\xd2y\xb3I9\t\xf6\xbfz\x86{\xba+\x145Z3#\xd2\xe8\xee_\xb2\x9b5eT\xbeE\xb1\x81 &!\x1am\xa9\xe8t\t\xda\x01\r\xde \xf6\xe8\x10\xe0\x07E'\xcf@\xbaD\xad\xabD\xb4\xc5P\xee\x9a\x00\x8a\x00\x16\x003\x00g\xc0\x9e\xc0\xa2\x00\x9e\x009\x00k\xc0\x9f\xc0\xa3\x00\x9f\x00E\x00\xbe\x00\x88\x00\xc4\x00\x9a\xc0\x08\xc0\t\xc0#\xc0\xac\xc0\xae\xc0+\xc0\n\xc0$\xc0\xad\xc0\xaf\xc0,\xc0r\xc0s\xcc\xa9\x13\x02\x13\x01\xcc\x14\xc0\x07\xc0\x12\xc0\x13\xc0'\xc0/\xc0\x14\xc0(\xc00\xc0`\xc0a\xc0v\xc0w\xcc\xa8\x13\x05\x13\x04\x13\x03\xcc\x13\xc0\x11\x00\n\x00/\x00<\xc0\x9c\xc0\xa0\x00\x9c\x005\x00=\xc0\x9d\xc0\xa1\x00\x9d\x00A\x00\xba\x00\x84\x00\xc0\x00\x07\x00\x04\x00\x05\x01\x00\x00\xde\x00\x00\x00!\x00\x1f\x00\x00\x1cjsonplaceholder.typicode.com\x00\x17\x00\x00\x00\x01\x00\x01\x01\xff\x01\x00\x01\x00\x00\n\x00\n\x00\x08\x00\x1d\x00\x17\x00\x18\x00\x19\x00\x0b\x00\x02\x01\x00\x00#\x00\x00\x00\x10\x00<\x00:\x02hq\x03h2c\x06spdy/3\x02h2\x06spdy/2\x06spdy/1\x08http/1.1\x08http/1.0\x08http/0.9\x00\r\x00\x14\x00\x12\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x02\x01\x003\x00&\x00$\x00\x1d\x00 Z]6\x15\xb6?\xd8\xd4\xd1]\xee\xc1\x81\x99\xfe\x01\xf3\t\xdf\x83\xae\xd7\x8b\xb4\xf5\t\x11\x17\x08\xda\x82\x01\x00-\x00\x02\x01\x01\x00+\x00\x07\x06\x03\x03\x03\x02\x03\x01";
        let expected_packet = b"\x16\x03\x03\x01\xb5\x01\x00\x01\xb1\x03\x03******************************** ********************************\x00\x8a\x00\x16\x003\x00g\xc0\x9e\xc0\xa2\x00\x9e\x009\x00k\xc0\x9f\xc0\xa3\x00\x9f\x00E\x00\xbe\x00\x88\x00\xc4\x00\x9a\xc0\x08\xc0\t\xc0#\xc0\xac\xc0\xae\xc0+\xc0\n\xc0$\xc0\xad\xc0\xaf\xc0,\xc0r\xc0s\xcc\xa9\x13\x02\x13\x01\xcc\x14\xc0\x07\xc0\x12\xc0\x13\xc0'\xc0/\xc0\x14\xc0(\xc00\xc0`\xc0a\xc0v\xc0w\xcc\xa8\x13\x05\x13\x04\x13\x03\xcc\x13\xc0\x11\x00\n\x00/\x00<\xc0\x9c\xc0\xa0\x00\x9c\x005\x00=\xc0\x9d\xc0\xa1\x00\x9d\x00A\x00\xba\x00\x84\x00\xc0\x00\x07\x00\x04\x00\x05\x01\x00\x00\xde\x00\x00\x00!\x00\x1f\x00\x00\x1cjsonplaceholder.typicode.com\x00\x17\x00\x00\x00\x01\x00\x01\x01\xff\x01\x00\x01\x00\x00\n\x00\n\x00\x08\x00\x1d\x00\x17\x00\x18\x00\x19\x00\x0b\x00\x02\x01\x00\x00#\x00\x00\x00\x10\x00<\x00:\x02hq\x03h2c\x06spdy/3\x02h2\x06spdy/2\x06spdy/1\x08http/1.1\x08http/1.0\x08http/0.9\x00\r\x00\x14\x00\x12\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x02\x01\x003\x00&\x00$\x00\x1d\x00 ********************************\x00-\x00\x02\x01\x01\x00+\x00\x07\x06\x03\x03\x03\x02\x03\x01".to_vec();

        let tls_1_2_spec = PacketSpecification {
            host: "jsonplaceholder.typicode.com".to_string(),
            port: "443".to_string(),
            tls_version: TlsVersion::TLS1_2,
            cipher_list: CipherList::ALL,
            cipher_order: CipherOrder::FORWARD,
            use_grease: false,
            use_rare_apln: false,
            tls_version_support: TlsVersionSupport::TLS1_2,
            extension_order: CipherOrder::REVERSE,
        };

        let packet = rust_jarm::build_packet(&tls_1_2_spec, &test_rng());
        assert_eq!(packet, expected_packet);
    }

    #[test]
    fn test_build_tls_1_2_reverse() {
        let expected_packet = b"\x16\x03\x03\x01\xb5\x01\x00\x01\xb1\x03\x03******************************** ********************************\x00\x8a\x00\x05\x00\x04\x00\x07\x00\xc0\x00\x84\x00\xba\x00A\x00\x9d\xc0\xa1\xc0\x9d\x00=\x005\x00\x9c\xc0\xa0\xc0\x9c\x00<\x00/\x00\n\xc0\x11\xcc\x13\x13\x03\x13\x04\x13\x05\xcc\xa8\xc0w\xc0v\xc0a\xc0`\xc00\xc0(\xc0\x14\xc0/\xc0'\xc0\x13\xc0\x12\xc0\x07\xcc\x14\x13\x01\x13\x02\xcc\xa9\xc0s\xc0r\xc0,\xc0\xaf\xc0\xad\xc0$\xc0\n\xc0+\xc0\xae\xc0\xac\xc0#\xc0\t\xc0\x08\x00\x9a\x00\xc4\x00\x88\x00\xbe\x00E\x00\x9f\xc0\xa3\xc0\x9f\x00k\x009\x00\x9e\xc0\xa2\xc0\x9e\x00g\x003\x00\x16\x01\x00\x00\xde\x00\x00\x00!\x00\x1f\x00\x00\x1cjsonplaceholder.typicode.com\x00\x17\x00\x00\x00\x01\x00\x01\x01\xff\x01\x00\x01\x00\x00\n\x00\n\x00\x08\x00\x1d\x00\x17\x00\x18\x00\x19\x00\x0b\x00\x02\x01\x00\x00#\x00\x00\x00\x10\x00<\x00:\x08http/0.9\x08http/1.0\x08http/1.1\x06spdy/1\x06spdy/2\x06spdy/3\x02h2\x03h2c\x02hq\x00\r\x00\x14\x00\x12\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x02\x01\x003\x00&\x00$\x00\x1d\x00 ********************************\x00-\x00\x02\x01\x01\x00+\x00\x07\x06\x03\x01\x03\x02\x03\x03".to_vec();

        let tls_1_2_spec = PacketSpecification {
            host: "jsonplaceholder.typicode.com".to_string(),
            port: "443".to_string(),
            tls_version: TlsVersion::TLS1_2,
            cipher_list: CipherList::ALL,
            cipher_order: CipherOrder::REVERSE,
            use_grease: false,
            use_rare_apln: false,
            tls_version_support: TlsVersionSupport::TLS1_2,
            extension_order: CipherOrder::FORWARD,
        };

        let packet = rust_jarm::build_packet(&tls_1_2_spec, &test_rng());
        assert_eq!(packet, expected_packet);
    }

    #[test]
    fn test_build_tls_1_2_top_half() {
        let expected_packet = b"\x16\x03\x03\x01f\x01\x00\x01b\x03\x03******************************** ********************************\x00F\xc0\x12\xc0\x07\xcc\x14\x13\x01\x13\x02\xcc\xa9\xc0s\xc0r\xc0,\xc0\xaf\xc0\xad\xc0$\xc0\n\xc0+\xc0\xae\xc0\xac\xc0#\xc0\t\xc0\x08\x00\x9a\x00\xc4\x00\x88\x00\xbe\x00E\x00\x9f\xc0\xa3\xc0\x9f\x00k\x009\x00\x9e\xc0\xa2\xc0\x9e\x00g\x003\x00\x16\x01\x00\x00\xd3\x00\x00\x00!\x00\x1f\x00\x00\x1cjsonplaceholder.typicode.com\x00\x17\x00\x00\x00\x01\x00\x01\x01\xff\x01\x00\x01\x00\x00\n\x00\n\x00\x08\x00\x1d\x00\x17\x00\x18\x00\x19\x00\x0b\x00\x02\x01\x00\x00#\x00\x00\x00\x10\x00<\x00:\x08http/0.9\x08http/1.0\x08http/1.1\x06spdy/1\x06spdy/2\x06spdy/3\x02h2\x03h2c\x02hq\x00\r\x00\x14\x00\x12\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x02\x01\x003\x00&\x00$\x00\x1d\x00 ********************************\x00-\x00\x02\x01\x01".to_vec();

        let tls_1_2_spec = PacketSpecification {
            host: "jsonplaceholder.typicode.com".to_string(),
            port: "443".to_string(),
            tls_version: TlsVersion::TLS1_2,
            cipher_list: CipherList::ALL,
            cipher_order: CipherOrder::TOP_HALF,
            use_grease: false,
            use_rare_apln: false,
            tls_version_support: TlsVersionSupport::NO_SUPPORT,
            extension_order: CipherOrder::FORWARD,
        };

        let packet = rust_jarm::build_packet(&tls_1_2_spec, &test_rng());
        assert_eq!(packet, expected_packet);
    }

    #[test]
    fn test_build_tls_1_2_bottom_half() {
        let expected_packet = b"\x16\x03\x03\x01X\x01\x00\x01T\x03\x03******************************** ********************************\x00D\xc0\x13\xc0'\xc0/\xc0\x14\xc0(\xc00\xc0`\xc0a\xc0v\xc0w\xcc\xa8\x13\x05\x13\x04\x13\x03\xcc\x13\xc0\x11\x00\n\x00/\x00<\xc0\x9c\xc0\xa0\x00\x9c\x005\x00=\xc0\x9d\xc0\xa1\x00\x9d\x00A\x00\xba\x00\x84\x00\xc0\x00\x07\x00\x04\x00\x05\x01\x00\x00\xc7\x00\x00\x00!\x00\x1f\x00\x00\x1cjsonplaceholder.typicode.com\x00\x17\x00\x00\x00\x01\x00\x01\x01\xff\x01\x00\x01\x00\x00\n\x00\n\x00\x08\x00\x1d\x00\x17\x00\x18\x00\x19\x00\x0b\x00\x02\x01\x00\x00#\x00\x00\x00\x10\x000\x00.\x08http/0.9\x08http/1.0\x06spdy/1\x06spdy/2\x06spdy/3\x03h2c\x02hq\x00\r\x00\x14\x00\x12\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x02\x01\x003\x00&\x00$\x00\x1d\x00 ********************************\x00-\x00\x02\x01\x01".to_vec();

        let tls_1_2_spec = PacketSpecification {
            host: "jsonplaceholder.typicode.com".to_string(),
            port: "443".to_string(),
            tls_version: TlsVersion::TLS1_2,
            cipher_list: CipherList::ALL,
            cipher_order: CipherOrder::BOTTOM_HALF,
            use_grease: false,
            use_rare_apln: true,
            tls_version_support: TlsVersionSupport::NO_SUPPORT,
            extension_order: CipherOrder::FORWARD,
        };

        let packet = rust_jarm::build_packet(&tls_1_2_spec, &test_rng());
        assert_eq!(packet, expected_packet);
    }

    #[test]
    fn test_build_tls_1_2_middle_out() {
        let expected_packet = b"\x16\x03\x03\x01\xa9\x01\x00\x01\xa5\x03\x03******************************** ********************************\x00\x8c\n\n\xc0\x12\xc0\x13\xc0\x07\xc0'\xcc\x14\xc0/\x13\x01\xc0\x14\x13\x02\xc0(\xcc\xa9\xc00\xc0s\xc0`\xc0r\xc0a\xc0,\xc0v\xc0\xaf\xc0w\xc0\xad\xcc\xa8\xc0$\x13\x05\xc0\n\x13\x04\xc0+\x13\x03\xc0\xae\xcc\x13\xc0\xac\xc0\x11\xc0#\x00\n\xc0\t\x00/\xc0\x08\x00<\x00\x9a\xc0\x9c\x00\xc4\xc0\xa0\x00\x88\x00\x9c\x00\xbe\x005\x00E\x00=\x00\x9f\xc0\x9d\xc0\xa3\xc0\xa1\xc0\x9f\x00\x9d\x00k\x00A\x009\x00\xba\x00\x9e\x00\x84\xc0\xa2\x00\xc0\xc0\x9e\x00\x07\x00g\x00\x04\x003\x00\x05\x00\x16\x01\x00\x00\xd0\n\n\x00\x00\x00\x00\x00!\x00\x1f\x00\x00\x1cjsonplaceholder.typicode.com\x00\x17\x00\x00\x00\x01\x00\x01\x01\xff\x01\x00\x01\x00\x00\n\x00\n\x00\x08\x00\x1d\x00\x17\x00\x18\x00\x19\x00\x0b\x00\x02\x01\x00\x00#\x00\x00\x00\x10\x000\x00.\x02hq\x03h2c\x06spdy/3\x06spdy/2\x06spdy/1\x08http/1.0\x08http/0.9\x00\r\x00\x14\x00\x12\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x02\x01\x003\x00+\x00)\n\n\x00\x01\x00\x00\x1d\x00 ********************************\x00-\x00\x02\x01\x01".to_vec();

        let tls_1_2_spec = PacketSpecification {
            host: "jsonplaceholder.typicode.com".to_string(),
            port: "443".to_string(),
            tls_version: TlsVersion::TLS1_2,
            cipher_list: CipherList::ALL,
            cipher_order: CipherOrder::MIDDLE_OUT,
            use_grease: true,
            use_rare_apln: true,
            tls_version_support: TlsVersionSupport::NO_SUPPORT,
            extension_order: CipherOrder::REVERSE,
        };

        let packet = rust_jarm::build_packet(&tls_1_2_spec, &test_rng());
        assert_eq!(packet, expected_packet);
    }

    #[test]
    fn test_build_tls_1_1() {
        let expected_packet = b"\x16\x03\x02\x01\xaa\x01\x00\x01\xa6\x03\x02******************************** ********************************\x00\x8a\x00\x16\x003\x00g\xc0\x9e\xc0\xa2\x00\x9e\x009\x00k\xc0\x9f\xc0\xa3\x00\x9f\x00E\x00\xbe\x00\x88\x00\xc4\x00\x9a\xc0\x08\xc0\t\xc0#\xc0\xac\xc0\xae\xc0+\xc0\n\xc0$\xc0\xad\xc0\xaf\xc0,\xc0r\xc0s\xcc\xa9\x13\x02\x13\x01\xcc\x14\xc0\x07\xc0\x12\xc0\x13\xc0'\xc0/\xc0\x14\xc0(\xc00\xc0`\xc0a\xc0v\xc0w\xcc\xa8\x13\x05\x13\x04\x13\x03\xcc\x13\xc0\x11\x00\n\x00/\x00<\xc0\x9c\xc0\xa0\x00\x9c\x005\x00=\xc0\x9d\xc0\xa1\x00\x9d\x00A\x00\xba\x00\x84\x00\xc0\x00\x07\x00\x04\x00\x05\x01\x00\x00\xd3\x00\x00\x00!\x00\x1f\x00\x00\x1cjsonplaceholder.typicode.com\x00\x17\x00\x00\x00\x01\x00\x01\x01\xff\x01\x00\x01\x00\x00\n\x00\n\x00\x08\x00\x1d\x00\x17\x00\x18\x00\x19\x00\x0b\x00\x02\x01\x00\x00#\x00\x00\x00\x10\x00<\x00:\x08http/0.9\x08http/1.0\x08http/1.1\x06spdy/1\x06spdy/2\x06spdy/3\x02h2\x03h2c\x02hq\x00\r\x00\x14\x00\x12\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x02\x01\x003\x00&\x00$\x00\x1d\x00 ********************************\x00-\x00\x02\x01\x01".to_vec();

        let tls_1_1_spec = PacketSpecification {
            host: "jsonplaceholder.typicode.com".to_string(),
            port: "443".to_string(),
            tls_version: TlsVersion::TLS1_1,
            cipher_list: CipherList::ALL,
            cipher_order: CipherOrder::FORWARD,
            use_grease: false,
            use_rare_apln: false,
            tls_version_support: TlsVersionSupport::NO_SUPPORT,
            extension_order: CipherOrder::FORWARD,
        };

        let packet = rust_jarm::build_packet(&tls_1_1_spec, &test_rng());
        assert_eq!(packet, expected_packet);
    }

    #[test]
    fn test_build_tls_1_3_forward() {
        let expected_packet = b"\x16\x03\x01\x01\xb7\x01\x00\x01\xb3\x03\x03******************************** ********************************\x00\x8a\x00\x16\x003\x00g\xc0\x9e\xc0\xa2\x00\x9e\x009\x00k\xc0\x9f\xc0\xa3\x00\x9f\x00E\x00\xbe\x00\x88\x00\xc4\x00\x9a\xc0\x08\xc0\t\xc0#\xc0\xac\xc0\xae\xc0+\xc0\n\xc0$\xc0\xad\xc0\xaf\xc0,\xc0r\xc0s\xcc\xa9\x13\x02\x13\x01\xcc\x14\xc0\x07\xc0\x12\xc0\x13\xc0'\xc0/\xc0\x14\xc0(\xc00\xc0`\xc0a\xc0v\xc0w\xcc\xa8\x13\x05\x13\x04\x13\x03\xcc\x13\xc0\x11\x00\n\x00/\x00<\xc0\x9c\xc0\xa0\x00\x9c\x005\x00=\xc0\x9d\xc0\xa1\x00\x9d\x00A\x00\xba\x00\x84\x00\xc0\x00\x07\x00\x04\x00\x05\x01\x00\x00\xe0\x00\x00\x00!\x00\x1f\x00\x00\x1cjsonplaceholder.typicode.com\x00\x17\x00\x00\x00\x01\x00\x01\x01\xff\x01\x00\x01\x00\x00\n\x00\n\x00\x08\x00\x1d\x00\x17\x00\x18\x00\x19\x00\x0b\x00\x02\x01\x00\x00#\x00\x00\x00\x10\x00<\x00:\x02hq\x03h2c\x06spdy/3\x02h2\x06spdy/2\x06spdy/1\x08http/1.1\x08http/1.0\x08http/0.9\x00\r\x00\x14\x00\x12\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x02\x01\x003\x00&\x00$\x00\x1d\x00 ********************************\x00-\x00\x02\x01\x01\x00+\x00\t\x08\x03\x04\x03\x03\x03\x02\x03\x01".to_vec();

        let tls_1_3_spec = PacketSpecification {
            host: "jsonplaceholder.typicode.com".to_string(),
            port: "443".to_string(),
            tls_version: TlsVersion::TLS1_3,
            cipher_list: CipherList::ALL,
            cipher_order: CipherOrder::FORWARD,
            use_grease: false,
            use_rare_apln: false,
            tls_version_support: TlsVersionSupport::TLS1_3,
            extension_order: CipherOrder::REVERSE,
        };

        let packet = rust_jarm::build_packet(&tls_1_3_spec, &test_rng());
        assert_eq!(packet, expected_packet);
    }

    #[test]
    fn test_build_tls_1_3_reverse() {
        let expected_packet = b"\x16\x03\x01\x01\xb7\x01\x00\x01\xb3\x03\x03******************************** ********************************\x00\x8a\x00\x05\x00\x04\x00\x07\x00\xc0\x00\x84\x00\xba\x00A\x00\x9d\xc0\xa1\xc0\x9d\x00=\x005\x00\x9c\xc0\xa0\xc0\x9c\x00<\x00/\x00\n\xc0\x11\xcc\x13\x13\x03\x13\x04\x13\x05\xcc\xa8\xc0w\xc0v\xc0a\xc0`\xc00\xc0(\xc0\x14\xc0/\xc0'\xc0\x13\xc0\x12\xc0\x07\xcc\x14\x13\x01\x13\x02\xcc\xa9\xc0s\xc0r\xc0,\xc0\xaf\xc0\xad\xc0$\xc0\n\xc0+\xc0\xae\xc0\xac\xc0#\xc0\t\xc0\x08\x00\x9a\x00\xc4\x00\x88\x00\xbe\x00E\x00\x9f\xc0\xa3\xc0\x9f\x00k\x009\x00\x9e\xc0\xa2\xc0\x9e\x00g\x003\x00\x16\x01\x00\x00\xe0\x00\x00\x00!\x00\x1f\x00\x00\x1cjsonplaceholder.typicode.com\x00\x17\x00\x00\x00\x01\x00\x01\x01\xff\x01\x00\x01\x00\x00\n\x00\n\x00\x08\x00\x1d\x00\x17\x00\x18\x00\x19\x00\x0b\x00\x02\x01\x00\x00#\x00\x00\x00\x10\x00<\x00:\x08http/0.9\x08http/1.0\x08http/1.1\x06spdy/1\x06spdy/2\x06spdy/3\x02h2\x03h2c\x02hq\x00\r\x00\x14\x00\x12\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x02\x01\x003\x00&\x00$\x00\x1d\x00 ********************************\x00-\x00\x02\x01\x01\x00+\x00\t\x08\x03\x01\x03\x02\x03\x03\x03\x04".to_vec();

        let tls_1_3_spec = PacketSpecification {
            host: "jsonplaceholder.typicode.com".to_string(),
            port: "443".to_string(),
            tls_version: TlsVersion::TLS1_3,
            cipher_list: CipherList::ALL,
            cipher_order: CipherOrder::REVERSE,
            use_grease: false,
            use_rare_apln: false,
            tls_version_support: TlsVersionSupport::TLS1_3,
            extension_order: CipherOrder::FORWARD,
        };

        let packet = rust_jarm::build_packet(&tls_1_3_spec, &test_rng());
        assert_eq!(packet, expected_packet);
    }

    #[test]
    fn test_build_tls_1_3_invalid() {
        let expected_packet = b"\x16\x03\x01\x01\xad\x01\x00\x01\xa9\x03\x03******************************** ********************************\x00\x80\x00\x16\x003\x00g\xc0\x9e\xc0\xa2\x00\x9e\x009\x00k\xc0\x9f\xc0\xa3\x00\x9f\x00E\x00\xbe\x00\x88\x00\xc4\x00\x9a\xc0\x08\xc0\t\xc0#\xc0\xac\xc0\xae\xc0+\xc0\n\xc0$\xc0\xad\xc0\xaf\xc0,\xc0r\xc0s\xcc\xa9\xcc\x14\xc0\x07\xc0\x12\xc0\x13\xc0'\xc0/\xc0\x14\xc0(\xc00\xc0`\xc0a\xc0v\xc0w\xcc\xa8\xcc\x13\xc0\x11\x00\n\x00/\x00<\xc0\x9c\xc0\xa0\x00\x9c\x005\x00=\xc0\x9d\xc0\xa1\x00\x9d\x00A\x00\xba\x00\x84\x00\xc0\x00\x07\x00\x04\x00\x05\x01\x00\x00\xe0\x00\x00\x00!\x00\x1f\x00\x00\x1cjsonplaceholder.typicode.com\x00\x17\x00\x00\x00\x01\x00\x01\x01\xff\x01\x00\x01\x00\x00\n\x00\n\x00\x08\x00\x1d\x00\x17\x00\x18\x00\x19\x00\x0b\x00\x02\x01\x00\x00#\x00\x00\x00\x10\x00<\x00:\x08http/0.9\x08http/1.0\x08http/1.1\x06spdy/1\x06spdy/2\x06spdy/3\x02h2\x03h2c\x02hq\x00\r\x00\x14\x00\x12\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x02\x01\x003\x00&\x00$\x00\x1d\x00 ********************************\x00-\x00\x02\x01\x01\x00+\x00\t\x08\x03\x01\x03\x02\x03\x03\x03\x04".to_vec();

        let tls_1_3_spec = PacketSpecification {
            host: "jsonplaceholder.typicode.com".to_string(),
            port: "443".to_string(),
            tls_version: TlsVersion::TLS1_3,
            cipher_list: CipherList::NO1_3,
            cipher_order: CipherOrder::FORWARD,
            use_grease: false,
            use_rare_apln: false,
            tls_version_support: TlsVersionSupport::TLS1_3,
            extension_order: CipherOrder::FORWARD,
        };

        let packet = rust_jarm::build_packet(&tls_1_3_spec, &test_rng());
        assert_eq!(packet, expected_packet);
    }

    #[test]
    fn test_build_tls_1_3_middle_out() {
        let expected_packet = b"\x16\x03\x01\x01\xc4\x01\x00\x01\xc0\x03\x03******************************** ********************************\x00\x8c\n\n\xc0\x12\xc0\x13\xc0\x07\xc0'\xcc\x14\xc0/\x13\x01\xc0\x14\x13\x02\xc0(\xcc\xa9\xc00\xc0s\xc0`\xc0r\xc0a\xc0,\xc0v\xc0\xaf\xc0w\xc0\xad\xcc\xa8\xc0$\x13\x05\xc0\n\x13\x04\xc0+\x13\x03\xc0\xae\xcc\x13\xc0\xac\xc0\x11\xc0#\x00\n\xc0\t\x00/\xc0\x08\x00<\x00\x9a\xc0\x9c\x00\xc4\xc0\xa0\x00\x88\x00\x9c\x00\xbe\x005\x00E\x00=\x00\x9f\xc0\x9d\xc0\xa3\xc0\xa1\xc0\x9f\x00\x9d\x00k\x00A\x009\x00\xba\x00\x9e\x00\x84\xc0\xa2\x00\xc0\xc0\x9e\x00\x07\x00g\x00\x04\x003\x00\x05\x00\x16\x01\x00\x00\xeb\n\n\x00\x00\x00\x00\x00!\x00\x1f\x00\x00\x1cjsonplaceholder.typicode.com\x00\x17\x00\x00\x00\x01\x00\x01\x01\xff\x01\x00\x01\x00\x00\n\x00\n\x00\x08\x00\x1d\x00\x17\x00\x18\x00\x19\x00\x0b\x00\x02\x01\x00\x00#\x00\x00\x00\x10\x00<\x00:\x02hq\x03h2c\x06spdy/3\x02h2\x06spdy/2\x06spdy/1\x08http/1.1\x08http/1.0\x08http/0.9\x00\r\x00\x14\x00\x12\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x02\x01\x003\x00+\x00)\n\n\x00\x01\x00\x00\x1d\x00 ********************************\x00-\x00\x02\x01\x01\x00+\x00\x0b\n\n\n\x03\x04\x03\x03\x03\x02\x03\x01".to_vec();

        let tls_1_3_spec = PacketSpecification {
            host: "jsonplaceholder.typicode.com".to_string(),
            port: "443".to_string(),
            tls_version: TlsVersion::TLS1_3,
            cipher_list: CipherList::ALL,
            cipher_order: CipherOrder::MIDDLE_OUT,
            use_grease: true,
            use_rare_apln: false,
            tls_version_support: TlsVersionSupport::TLS1_3,
            extension_order: CipherOrder::REVERSE,
        };

        let packet = rust_jarm::build_packet(&tls_1_3_spec, &test_rng());
        assert_eq!(packet, expected_packet);
    }

    #[test]
    fn test_get_ciphers_tls_1_2() {
        let expected_ciphers = b"\x00\x16\x003\x00g\xc0\x9e\xc0\xa2\x00\x9e\x009\x00k\xc0\x9f\xc0\xa3\x00\x9f\x00E\x00\xbe\x00\x88\x00\xc4\x00\x9a\xc0\x08\xc0\t\xc0#\xc0\xac\xc0\xae\xc0+\xc0\n\xc0$\xc0\xad\xc0\xaf\xc0,\xc0r\xc0s\xcc\xa9\x13\x02\x13\x01\xcc\x14\xc0\x07\xc0\x12\xc0\x13\xc0'\xc0/\xc0\x14\xc0(\xc00\xc0`\xc0a\xc0v\xc0w\xcc\xa8\x13\x05\x13\x04\x13\x03\xcc\x13\xc0\x11\x00\n\x00/\x00<\xc0\x9c\xc0\xa0\x00\x9c\x005\x00=\xc0\x9d\xc0\xa1\x00\x9d\x00A\x00\xba\x00\x84\x00\xc0\x00\x07\x00\x04\x00\x05".to_vec();

        let tls_1_2_spec = PacketSpecification {
            host: "jsonplaceholder.typicode.com".to_string(),
            port: "443".to_string(),
            tls_version: TlsVersion::TLS1_2,
            cipher_list: CipherList::ALL,
            cipher_order: CipherOrder::FORWARD,
            use_grease: false,
            use_rare_apln: false,
            tls_version_support: TlsVersionSupport::TLS1_2,
            extension_order: CipherOrder::REVERSE,
        };

        let packet = rust_jarm::get_ciphers(&tls_1_2_spec, &test_rng());
        assert_eq!(packet, expected_ciphers);
    }

    #[test]
    fn test_get_ciphers_tls_1_2_middle_out_and_grease() {
        let expected_ciphers = b"\n\n\xc0\x12\xc0\x13\xc0\x07\xc0'\xcc\x14\xc0/\x13\x01\xc0\x14\x13\x02\xc0(\xcc\xa9\xc00\xc0s\xc0`\xc0r\xc0a\xc0,\xc0v\xc0\xaf\xc0w\xc0\xad\xcc\xa8\xc0$\x13\x05\xc0\n\x13\x04\xc0+\x13\x03\xc0\xae\xcc\x13\xc0\xac\xc0\x11\xc0#\x00\n\xc0\t\x00/\xc0\x08\x00<\x00\x9a\xc0\x9c\x00\xc4\xc0\xa0\x00\x88\x00\x9c\x00\xbe\x005\x00E\x00=\x00\x9f\xc0\x9d\xc0\xa3\xc0\xa1\xc0\x9f\x00\x9d\x00k\x00A\x009\x00\xba\x00\x9e\x00\x84\xc0\xa2\x00\xc0\xc0\x9e\x00\x07\x00g\x00\x04\x003\x00\x05\x00\x16".to_vec();

        let tls_1_2_spec = PacketSpecification {
            host: "jsonplaceholder.typicode.com".to_string(),
            port: "443".to_string(),
            tls_version: TlsVersion::TLS1_2,
            cipher_list: CipherList::ALL,
            cipher_order: CipherOrder::MIDDLE_OUT,
            use_grease: true,
            use_rare_apln: true,
            tls_version_support: TlsVersionSupport::NO_SUPPORT,
            extension_order: CipherOrder::REVERSE,
        };

        let packet = rust_jarm::get_ciphers(&tls_1_2_spec, &test_rng());
        assert_eq!(packet, expected_ciphers);
    }

    #[test]
    fn test_get_extensions() {
        let expected_extensions = b"\x00\xde\x00\x00\x00!\x00\x1f\x00\x00\x1cjsonplaceholder.typicode.com\x00\x17\x00\x00\x00\x01\x00\x01\x01\xff\x01\x00\x01\x00\x00\n\x00\n\x00\x08\x00\x1d\x00\x17\x00\x18\x00\x19\x00\x0b\x00\x02\x01\x00\x00#\x00\x00\x00\x10\x00<\x00:\x02hq\x03h2c\x06spdy/3\x02h2\x06spdy/2\x06spdy/1\x08http/1.1\x08http/1.0\x08http/0.9\x00\r\x00\x14\x00\x12\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x02\x01\x003\x00&\x00$\x00\x1d\x00 ********************************\x00-\x00\x02\x01\x01\x00+\x00\x07\x06\x03\x03\x03\x02\x03\x01".to_vec();

        let tls_1_2_spec = PacketSpecification {
            host: "jsonplaceholder.typicode.com".to_string(),
            port: "443".to_string(),
            tls_version: TlsVersion::TLS1_2,
            cipher_list: CipherList::ALL,
            cipher_order: CipherOrder::FORWARD,
            use_grease: false,
            use_rare_apln: false,
            tls_version_support: TlsVersionSupport::TLS1_2,
            extension_order: CipherOrder::REVERSE,
        };

        let packet = rust_jarm::get_extensions(&tls_1_2_spec, &test_rng());
        assert_eq!(packet, expected_extensions);
    }

    #[test]
    fn test_extension_server_name() {
        let expected_extension_server_name = b"\x00\x00\x00!\x00\x1f\x00\x00\x1cjsonplaceholder.typicode.com".to_vec();

        let tls_1_2_spec = PacketSpecification {
            host: "jsonplaceholder.typicode.com".to_string(),
            port: "443".to_string(),
            tls_version: TlsVersion::TLS1_2,
            cipher_list: CipherList::ALL,
            cipher_order: CipherOrder::FORWARD,
            use_grease: false,
            use_rare_apln: false,
            tls_version_support: TlsVersionSupport::TLS1_2,
            extension_order: CipherOrder::REVERSE,
        };

        let packet = rust_jarm::extension_server_name(&tls_1_2_spec);
        assert_eq!(packet, expected_extension_server_name);
    }

    #[test]
    fn test_alpns() {
        let expected_alpns = b"\x00\x10\x00<\x00:\x02hq\x03h2c\x06spdy/3\x02h2\x06spdy/2\x06spdy/1\x08http/1.1\x08http/1.0\x08http/0.9".to_vec();

        let tls_1_2_spec = PacketSpecification {
            host: "jsonplaceholder.typicode.com".to_string(),
            port: "443".to_string(),
            tls_version: TlsVersion::TLS1_2,
            cipher_list: CipherList::ALL,
            cipher_order: CipherOrder::FORWARD,
            use_grease: false,
            use_rare_apln: false,
            tls_version_support: TlsVersionSupport::TLS1_2,
            extension_order: CipherOrder::REVERSE,
        };

        let packet = rust_jarm::aplns(&tls_1_2_spec);
        assert_eq!(packet, expected_alpns);
    }

    #[test]
    fn test_cipher_mung_forward() {
        let mut input_ciphers = vec![
            b"\x08http/0.9".to_vec(),
            b"\x08http/1.0".to_vec(),
            b"\x08http/1.1".to_vec(),
            b"\x06spdy/1".to_vec(),
            b"\x06spdy/2".to_vec(),
            b"\x06spdy/3\x02h2".to_vec(),
            b"\x03h2c".to_vec(),
            b"\x02hq".to_vec(),
        ];
        let original = input_ciphers.clone();

        rust_jarm::cipher_mung(&mut input_ciphers, &CipherOrder::FORWARD);
        assert_eq!(input_ciphers, original);  // Nothing changed for Forward order
    }

    #[test]
    fn test_cipher_mung_reverse() {
        let mut input_ciphers = vec![
            b"\x08http/0.9".to_vec(),
            b"\x08http/1.0".to_vec(),
            b"\x08http/1.1".to_vec(),
            b"\x06spdy/1".to_vec(),
            b"\x06spdy/2".to_vec(),
            b"\x06spdy/3\x02h2".to_vec(),
            b"\x03h2c".to_vec(),
            b"\x02hq".to_vec(),
        ];
        let expected_ciphers_output = vec![
            b"\x02hq".to_vec(),
            b"\x03h2c".to_vec(),
            b"\x06spdy/3\x02h2".to_vec(),
            b"\x06spdy/2".to_vec(),
            b"\x06spdy/1".to_vec(),
            b"\x08http/1.1".to_vec(),
            b"\x08http/1.0".to_vec(),
            b"\x08http/0.9".to_vec(),
        ];

        rust_jarm::cipher_mung(&mut input_ciphers, &CipherOrder::REVERSE);
        assert_eq!(input_ciphers, expected_ciphers_output);
    }

    #[test]
    fn test_cipher_mung_top_half() {
        let mut input_ciphers = vec![
            b"\x02hq".to_vec(),
            b"\x03h2c".to_vec(),
            b"\x06spdy/3\x02h2".to_vec(),
            b"\x06spdy/2".to_vec(),
            b"\x06spdy/1".to_vec(),
            b"\x08http/1.1".to_vec(),
            b"\x08http/1.0".to_vec(),
            b"\x08http/0.9".to_vec(),
        ];

        let expected_ciphers_output = vec![
            b"\x06spdy/2".to_vec(),
            b"\x06spdy/3\x02h2".to_vec(),
            b"\x03h2c".to_vec(),
            b"\x02hq".to_vec(),
        ];

        rust_jarm::cipher_mung(&mut input_ciphers, &CipherOrder::TOP_HALF);
        assert_eq!(input_ciphers, expected_ciphers_output);
    }

    #[test]
    fn test_cipher_mung_top_half_odd() {
        let mut input_ciphers = vec![
            b"\x02hq".to_vec(),
            b"\x03h2c".to_vec(),
            b"\x06spdy/3\x02h2".to_vec(),
            b"\x06spdy/2".to_vec(),
            b"\x06spdy/1".to_vec(),
            b"\x08http/1.1".to_vec(),
            b"\x08http/1.0".to_vec(),
        ];

        let expected_ciphers_output = vec![
            b"\x06spdy/2".to_vec(),
            b"\x06spdy/3\x02h2".to_vec(),
            b"\x03h2c".to_vec(),
            b"\x02hq".to_vec(),
        ];

        rust_jarm::cipher_mung(&mut input_ciphers, &CipherOrder::TOP_HALF);
        assert_eq!(input_ciphers, expected_ciphers_output);
    }

    #[test]
    fn test_cipher_mung_bottom_half() {
        let mut input_ciphers = vec![
            b"\x02hq".to_vec(),
            b"\x03h2c".to_vec(),
            b"\x06spdy/3\x02h2".to_vec(),
            b"\x06spdy/2".to_vec(),
            b"\x06spdy/1".to_vec(),
            b"\x08http/1.1".to_vec(),
            b"\x08http/1.0".to_vec(),
            b"\x08http/0.9".to_vec(),
        ];

        let expected_ciphers_output = vec![
            b"\x06spdy/1".to_vec(),
            b"\x08http/1.1".to_vec(),
            b"\x08http/1.0".to_vec(),
            b"\x08http/0.9".to_vec(),
        ];

        rust_jarm::cipher_mung(&mut input_ciphers, &CipherOrder::BOTTOM_HALF);
        assert_eq!(input_ciphers, expected_ciphers_output);
    }

    #[test]
    fn test_cipher_mung_bottom_half_odd() {
        let mut input_ciphers = vec![
            b"\x02hq".to_vec(),
            b"\x03h2c".to_vec(),
            b"\x06spdy/3\x02h2".to_vec(),
            b"\x06spdy/2".to_vec(),
            b"\x06spdy/1".to_vec(),
            b"\x08http/1.1".to_vec(),
            b"\x08http/1.0".to_vec(),
        ];

        let expected_ciphers_output = vec![
            b"\x06spdy/1".to_vec(),
            b"\x08http/1.1".to_vec(),
            b"\x08http/1.0".to_vec(),
        ];

        rust_jarm::cipher_mung(&mut input_ciphers, &CipherOrder::BOTTOM_HALF);
        assert_eq!(input_ciphers, expected_ciphers_output);
    }

    #[test]
    fn test_cipher_mung_middle_out() {
        let mut input_ciphers = vec![
            b"\x02hq".to_vec(),
            b"\x03h2c".to_vec(),
            b"\x06spdy/3\x02h2".to_vec(),
            b"\x06spdy/2".to_vec(),
            b"\x06spdy/1".to_vec(),
            b"\x08http/1.1".to_vec(),
            b"\x08http/1.0".to_vec(),
            b"\x08http/0.9".to_vec(),
        ];

        let expected_ciphers_output = vec![
            b"\x06spdy/1".to_vec(),
            b"\x06spdy/2".to_vec(),
            b"\x08http/1.1".to_vec(),
            b"\x06spdy/3\x02h2".to_vec(),
            b"\x08http/1.0".to_vec(),
            b"\x03h2c".to_vec(),
            b"\x08http/0.9".to_vec(),
            b"\x02hq".to_vec(),
        ];

        rust_jarm::cipher_mung(&mut input_ciphers, &CipherOrder::MIDDLE_OUT);
        assert_eq!(input_ciphers, expected_ciphers_output);
    }

    #[test]
    fn test_cipher_mung_middle_out_odd() {
        let mut input_ciphers = vec![
            b"\x02hq".to_vec(),
            b"\x03h2c".to_vec(),
            b"\x06spdy/3\x02h2".to_vec(),
            b"\x06spdy/2".to_vec(),
            b"\x06spdy/1".to_vec(),
            b"\x08http/1.1".to_vec(),
            b"\x08http/1.0".to_vec(),
        ];

        let expected_ciphers_output = vec![
            b"\x06spdy/2".to_vec(),
            b"\x06spdy/1".to_vec(),
            b"\x06spdy/3\x02h2".to_vec(),
            b"\x08http/1.1".to_vec(),
            b"\x03h2c".to_vec(),
            b"\x08http/1.0".to_vec(),
            b"\x02hq".to_vec(),
        ];

        rust_jarm::cipher_mung(&mut input_ciphers, &CipherOrder::MIDDLE_OUT);
        assert_eq!(input_ciphers, expected_ciphers_output);
    }

    #[test]
    fn test_key_share() {
        let expected_key_share = b"\x003\x00&\x00$\x00\x1d\x00 ********************************".to_vec();
        assert_eq!(rust_jarm::key_share(false, &test_rng()), expected_key_share);
    }

    #[test]
    fn test_supported_versions() {
        let expected_supported_versions = b"\x00+\x00\x07\x06\x03\x03\x03\x02\x03\x01".to_vec();

        let tls_1_2_spec = PacketSpecification {
            host: "jsonplaceholder.typicode.com".to_string(),
            port: "443".to_string(),
            tls_version: TlsVersion::TLS1_2,
            cipher_list: CipherList::ALL,
            cipher_order: CipherOrder::FORWARD,
            use_grease: false,
            use_rare_apln: false,
            tls_version_support: TlsVersionSupport::TLS1_2,
            extension_order: CipherOrder::REVERSE,
        };

        assert_eq!(rust_jarm::supported_versions(&tls_1_2_spec, &test_rng()), expected_supported_versions);
    }

    #[test]
    fn test_read_packet_tls_1_2() {
        let input_hex = "160303004c0200004803035ffb8b2d1d50e207efcff257647b8cb319bd10a920b6968d444f574e4752440100c02b0000200000000000170000ff01000100000b000201000023000000100005000302683216030308a50b0008a100089e0004c7308204c330820469a003020102021003f93e0cd51ed9e174d552a522425dba300a06082a8648ce3d040302304a310b300906035504061302555331193017060355040a1310436c6f7564666c6172652c20496e632e3120301e06035504031317436c6f7564666c61726520496e63204543432043412d33301e170d3230303732393030303030305a170d3231303732393132303030305a306d310b3009060355040613025553310b3009060355040813024341311630140603550407130d53616e204672616e636973636f31193017060355040a1310436c6f7564666c6172652c20496e632e311e301c06035504031315736e692e636c6f7564666c61726573736c2e636f6d3059301306072a8648ce3d020106082a8648ce3d03010703420004d73c51db4658abcb9d7ab52ff121496eb4c7e8e985d8742b20cef649c6e4ad1a692c44a12964c289bc2bd4aa22d767a0e7f95802de915a05e0ede1b4b9ce9636a382030c30820308301f0603551d23041830168014a5ce37eaebb0750e946788b445fad9241087961f301d0603551d0e0416041455da5417da45572aac6f8b2988693e361b204b75303e0603551d1104373035820e2a2e74797069636f64652e636f6d8215736e692e636c6f7564666c61726573736c2e636f6d820c74797069636f64652e636f6d300e0603551d0f0101ff040403020780301d0603551d250416301406082b0601050507030106082b06010505070302307b0603551d1f047430723037a035a0338631687474703a2f2f63726c332e64696769636572742e636f6d2f436c6f7564666c617265496e6345434343412d332e63726c3037a035a0338631687474703a2f2f63726c342e64696769636572742e636f6d2f436c6f7564666c617265496e6345434343412d332e63726c304c0603551d2004453043303706096086480186fd6c0101302a302806082b06010505070201161c68747470733a2f2f7777772e64696769636572742e636f6d2f4350533008060667810c010202307606082b06010505070101046a3068302406082b060105050730018618687474703a2f2f6f6373702e64696769636572742e636f6d304006082b060105050730028634687474703a2f2f636163657274732e64696769636572742e636f6d2f436c6f7564666c617265496e6345434343412d332e637274300c0603551d130101ff0402300030820104060a2b06010401d6790204020481f50481f200f0007600f65c942fd1773022145418083094568ee34d131933bfdf0c2f200bcc4ef164e3000001739b984538000004030047304502200221d2639b0c1f5ee1e66e4ddaf2c54e42833539f859f9c24407bec51f8ed759022100a36a3758696d4f734a432d2a20434c3b231747a4a71510dc468bdd3b5ca731d90076005cdc4392fee6ab4544b15e9ad456e61037fbd5fa47dca17394b25ee6f6c70eca000001739b9845670000040300473045022100eb62006e7d9149f9b6df5b2f00b588e890a0842c8bda890c41db7b7ebba9bfe20220209f2fac15cc45bcc4cad77a560a1d3bfd1fccb72ade6d423fe8f464633826fa300a06082a8648ce3d0403020348003045022100833607c623fcd6d6f159c3e06ab582031b4918c734ffeb9362ebe034de33f78202200fd50b4eaafb4ade2ae16f39d94c2da629d7a05a3bc1d0e9b943ed03b0840dda0003d1308203cd308202b5a00302010202100a3787645e5fb48c224efd1bed140c3c300d06092a864886f70d01010b0500305a310b300906035504061302494531123010060355040a130942616c74696d6f726531133011060355040b130a43796265725472757374312230200603550403131942616c74696d6f7265204379626572547275737420526f6f74301e170d3230303132373132343830385a170d3234313233";
        let input_packet = hex::decode(input_hex).unwrap();
        let expected_result = "c02b|0303|h2|0000-0017-ff01-000b-0023-0010";

        let jarm = rust_jarm::read_packet(input_packet);

        assert_eq!(jarm.raw, expected_result);
    }

    #[test]
    fn test_pack_as_unsigned_char() {
        assert_eq!(rust_jarm::pack_as_unsigned_char(1), b'\x01');
        assert_eq!(rust_jarm::pack_as_unsigned_char(32), b' ');
        assert_eq!(rust_jarm::pack_as_unsigned_char(45), b'-');
        assert_eq!(rust_jarm::pack_as_unsigned_char(102), b'f');
    }

    #[test]
    fn test_pack_as_unsigned_short() {
        eprintln!("\x00\x01 = {:?}", b"\x00\x01".to_vec());
        eprintln!("\x00 = {:?}", b"\x00");
        assert_eq!(rust_jarm::pack_as_unsigned_short(1), b"\x00\x01".to_vec());
        assert_eq!(rust_jarm::pack_as_unsigned_short(32), b"\x00 ".to_vec());
        assert_eq!(rust_jarm::pack_as_unsigned_short(45), b"\x00-".to_vec());
        assert_eq!(rust_jarm::pack_as_unsigned_short(102), b"\x00f".to_vec());
        assert_eq!(rust_jarm::pack_as_unsigned_short(1020), b"\x03\xfc".to_vec());
    }

    #[test]
    fn test_as_u32_be() {
        assert_eq!(rust_jarm::as_u32_be(&[0, 1]), 1);
        assert_eq!(rust_jarm::as_u32_be(&[1, 0]), 256);
        assert_eq!(rust_jarm::as_u32_be(&[4, 7]), 1031);
    }

    #[test]
    fn test_mocked_random_bytes() {
        let expected_mock_value: Vec<u8> = b"********************************".to_vec();
        let rng = TestRng {};
        assert_eq!(rng.random_bytes(), expected_mock_value);
    }

    #[test]
    fn test_mocked_random_grease() {
        let expected_mock_value: Vec<u8> = b"\x0a\x0a".to_vec();
        let rng = TestRng {};
        assert_eq!(rng.random_grease(), expected_mock_value);
    }

    #[test]
    fn test_find_extension() {  // TODO add more example
        let types: Vec<&[u8]> = vec![b"\x00\x00", b"\x00\x17", b"\xff\x01", b"\x00\x0b", b"\x00#", b"\x00\x10"];
        let values: Vec<Option<&[u8]>> = vec![None, None, Some(b"\x00"), Some(b"\x01\x00"), None, Some(b"\x00\x03\x02h2")];
        let expected_result = "h2".to_string();

        let result = rust_jarm::find_extension(&types, values);

        assert_eq!(result, expected_result);
    }

    #[test]
    fn test_add_formatting_hyphen() {
        let types: Vec<&[u8]> = vec![b"\x00\x00", b"\x00\x17", b"\xff\x01", b"\x00\x0b", b"\x00#", b"\x00\x10"];
        let expected_result = "0000-0017-ff01-000b-0023-0010".to_string();

        let result = rust_jarm::add_formatting_hyphen(&types);

        assert_eq!(result, expected_result);
    }

    fn server_packet_fixture() -> String {
        String::from("160303004c0200004803035fff763e66828f14f1107414d298002a2a6cef45158d699a444f574e4752440100c02b0000200000000000170000ff01000100000b000201000023000000100005000302683216030308a50b0008a100089e0004c7308204c330820469a003020102021003f93e0cd51ed9e174d552a522425dba300a06082a8648ce3d040302304a310b300906035504061302555331193017060355040a1310436c6f7564666c6172652c20496e632e3120301e06035504031317436c6f7564666c61726520496e63204543432043412d33301e170d3230303732393030303030305a170d3231303732393132303030305a306d310b3009060355040613025553310b3009060355040813024341311630140603550407130d53616e204672616e636973636f31193017060355040a1310436c6f7564666c6172652c20496e632e311e301c06035504031315736e692e636c6f7564666c61726573736c2e636f6d3059301306072a8648ce3d020106082a8648ce3d03010703420004d73c51db4658abcb9d7ab52ff121496eb4c7e8e985d8742b20cef649c6e4ad1a692c44a12964c289bc2bd4aa22d767a0e7f95802de915a05e0ede1b4b9ce9636a382030c30820308301f0603551d23041830168014a5ce37eaebb0750e946788b445fad9241087961f301d0603551d0e0416041455da5417da45572aac6f8b2988693e361b204b75303e0603551d1104373035820e2a2e74797069636f64652e636f6d8215736e692e636c6f7564666c61726573736c2e636f6d820c74797069636f64652e636f6d300e0603551d0f0101ff040403020780301d0603551d250416301406082b0601050507030106082b06010505070302307b0603551d1f047430723037a035a0338631687474703a2f2f63726c332e64696769636572742e636f6d2f436c6f7564666c617265496e6345434343412d332e63726c3037a035a0338631687474703a2f2f63726c342e64696769636572742e636f6d2f436c6f7564666c617265496e6345434343412d332e63726c304c0603551d2004453043303706096086480186fd6c0101302a302806082b06010505070201161c68747470733a2f2f7777772e64696769636572742e636f6d2f4350533008060667810c010202307606082b06010505070101046a3068302406082b060105050730018618687474703a2f2f6f6373702e64696769636572742e636f6d304006082b060105050730028634687474703a2f2f636163657274732e64696769636572742e636f6d2f436c6f7564666c617265496e6345434343412d332e637274300c0603551d130101ff0402300030820104060a2b06010401d6790204020481f50481f200f0007600f65c942fd1773022145418083094568ee34d131933bfdf0c2f200bcc4ef164e3000001739b984538000004030047304502200221d2639b0c1f5ee1e66e4ddaf2c54e42833539f859f9c24407bec51f8ed759022100a36a3758696d4f734a432d2a20434c3b231747a4a71510dc468bdd3b5ca731d90076005cdc4392fee6ab4544b15e9ad456e61037fbd5fa47dca17394b25ee6f6c70eca000001739b9845670000040300473045022100eb62006e7d9149f9b6df5b2f00b588e890a0842c8bda890c41db7b7ebba9bfe20220209f2fac15cc45bcc4cad77a560a1d3bfd1fccb72ade6d423fe8f464633826fa300a06082a8648ce3d0403020348003045022100833607c623fcd6d6f159c3e06ab582031b4918c734ffeb9362ebe034de33f78202200fd50b4eaafb4ade2ae16f39d94c2da629d7a05a3bc1d0e9b943ed03b0840dda0003d1308203cd308202b5a00302010202100a3787645e5fb48c224efd1bed140c3c300d06092a864886f70d01010b0500305a310b300906035504061302494531123010060355040a130942616c74696d6f726531133011060355040b130a43796265725472757374312230200603550403131942616c74696d6f72652043796265725472")
    }

    #[test]
    fn test_extract_extension_info() {
        let expected_result = "h2|0000-0017-ff01-000b-0023-0010";
        let input_hex = server_packet_fixture();
        let input_packet = hex::decode(input_hex).unwrap();
        let counter: usize = 0;

        let extension = rust_jarm::extract_extension_info(input_packet, counter);

        assert_eq!(extension, expected_result);
    }

    #[test]
    fn test_extract_extension_info_non_zero_counter() {
        let expected_result = "|0033-002b";
        let input_hex = "160303007a0200007603031d9f05dd4890d4d2d6f1e52ac85ebbe9ef513ae1dec929184898e79fbb85f6c1202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a130200002e00330024001d0020f55d8616ecdb3aede2b32ec195e1ce736063addc26b3a3a2066d9b1395faef33002b0002030414030300010117030309520c60b16e828fcbb0f4e7b7eff58472a0813912b1d5c92dc7d38d31ae5a226f9616ad8ddcc46716aaac7af43e29811d6c91aa3c45b5ce681297ce7d8856e78833f52ef6980da27f9cd763880e12472b56b181c67d7d68b270494f3bd4aa63951d2a3be43e5f49974d6a3e1466f7be7142c77ef768890444a9a08e7dd582b8ddfe04548e3da334e7480a818ca920360e318904fee523096381ef5b90500c7eb9e60d71d2d3291ca96d7dd966c117d1068685e878ff105330c810615fb257481e12ce616dbdd442124881c3b60f237252c5d1c863457517e4f837fd07db84bd28414ba9b67adcc4386310e861d5506b3cd2bfda4b0ece9451afe369e44ae4c3632c1f2a5098098677bb17d8af11350c99bda022d35f95bec0f0e2a19776b321a65942147d1b2cc95f468b9995bd08e0a1f36b912715fdad4c19b278545000c143171c7ff0929ee79f3ee6617014d5619bcf326d5017029bd559de91aedc32d879f1a9ae7642af4dc704fea1b94bea5b3c83265ac17aba12dde5bec9333a3bc6ff8ff3f232a7470c03cf0c465e0d22634f8fdc5e4d074fa583eb8cb928c1fbce39ba1e3851bb942cb3e56fe9c01ea908e196f9ab657bb3d3890192b7c6a4b5dfd12cd360adf1411ecec667bb3a6aa436449e14dc5ca44eb51723c93e3c009e840bf806d8f86a096d52409cc0b65ef5e0833ba7a88881fe5cff74158f5907744b36d83a183b20ae7cc9a2a2175e9cd8fc41d7e98823f295868b47c1fc7d80efd8ea7ca2c75d0a5d460e5f2cc77540a325a2e15be0496154c4a020737f222e64fdca274097d3c4fd6166ee20c6e7dcc3ca22e45153d075b5e4a82c5525a622a53d22145ac0115f704f6c46db34849bb75c5ed081c4881a596f2482dbaccdb122308fa85801c0c7afcfdceae7bd96c93ac6b4d341cefa3d21b032bb542473a85b091f43baaf195182d1ebc632c9fd2a232ae6183f689b324c550a58ad2c184803504802f71224015f9ec0697f87732e67b4fb3f8ff6a1a19a256bef9364bf81b5b53a71ee43f53be7920ea5820d43277a8140f853a0b5122ccbc4c321af0ebc688294869cbc29a35a87f197288b01a003b88d3e327c77f586c4c3746f6c6763fe0395885b4650a0b8b84d4feb3cb3c481d6d530756d1de4a517393d1669957da443bbc5fb2369595fef8272b17c69ad3e7e66a05f01cf2e194127f88164fb8bf0672c624df9a8462138854c13b7e63d089b9607b1aa86336b17cdb82a68448a51af56911664e87f2e9d04d16132744a5211fc6063ae46b84ba10ab2cd5333406cdaf9bd9c4bd3514fd45305e1bbb24c2df4467ebf5acb2c1fae1ee9e4db8ab464dd0222f7076d7d2e6939bf0c60b2a6cd9c4b5dc0cad0cc5084292b8f3848eda34366e9dad15d8e30b3a64ca67d7f6e26a304331e5a445fc55f76469090728df465471be9320c5d254e32b22a622709ff2427ad852d802a48333165e3615b78b81169b23e4efea11fe353270110db0ee7343af6c4c4ac390e8702e4f175cb25aebb63327482b40b6a60f5d7bb50baa34273a0256097a362bd384e209dcd86732aa7f10cc015d41fe29f3c321f95af23837dfedab6f7a29b686de6f5d7d3ef6e1c72fd6aa83eb6c44f099a8ee68f9d75c8855d9d1b0277d9a427ff6282bf996bb7d2c12d0c4785b8c2c2fc51438d1599e88cdd4c48eddab8ae8cedbb59e41de0536fd68fc454fecea1b289af5b9a1f8874f6b28ac96430765fb455d0f3a40c7fbcc9cde9a019b3b3c1fb30d8c9431dd1a56463bfb816b85ee6ba046d930e2b1d99b9c020b59e65276d6000f7c91784f391233508167407278a548904fd0777f2e2fd581fbda2701e6435eb60bcf6bad0f64c1ab14cf82e03ee1ae6fe752e558b37e263fd0c61";
        let input_packet = hex::decode(input_hex).unwrap();
        let counter: usize = 32;

        let extension = rust_jarm::extract_extension_info(input_packet, counter);

        assert_eq!(extension, expected_result);
    }

    #[test]
    fn test_extract_extension_info_incorrect_server_hello_length() {
        let expected_result = "|";
        let input_hex = "160303004a02000046030363b1b9e1dc4c5d4d8a9998d4d939ebf07c1d2499991ee35b8fd8871e642e824e202e6cceb91ca5f1f8f18749fb00394deb136e0f08fea88b8902547ba7e8dbe5bac02f0016030300070b00000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
        let input_packet = hex::decode(input_hex).unwrap();
        let counter: usize = 32;

        let extension = rust_jarm::extract_extension_info(input_packet, counter);

        assert_eq!(extension, expected_result);
    }

    #[test]
    fn test_extract_extension_info_length_start_is_11() {
        let expected_result = "|";
        let input_hex = server_packet_fixture();
        let mut input_packet = hex::decode(input_hex).unwrap();


        let counter: usize = 32;
        let length_start_index = counter + 47;
        input_packet[length_start_index] = 11;  // Force the value to 11

        let extension = rust_jarm::extract_extension_info(input_packet, counter);

        assert_eq!(extension, expected_result);
    }

    #[test]
    fn test_extract_extension_info_incoming_error_1() {
        let expected_result = "|";
        let input_hex = server_packet_fixture();
        let mut input_packet = hex::decode(input_hex).unwrap();

        let counter: usize = 32;
        // Force error value
        let incoming_error_1 = b"\x0e\xac\x0b".to_vec();
        input_packet[(counter+50)..(counter+53)].copy_from_slice(&incoming_error_1[..]);

        let extension = rust_jarm::extract_extension_info(input_packet, counter);

        assert_eq!(extension, expected_result);
    }

    #[test]
    fn test_extract_extension_info_incoming_error_2() {
        let expected_result = "|";
        let input_hex = server_packet_fixture();
        let mut input_packet = hex::decode(input_hex).unwrap();

        let counter: usize = 32;
        // Force error value
        let incoming_error_2 = b"\x0f\xf0\x0b".to_vec();
        input_packet[(counter +82)..(counter +85)].copy_from_slice(&incoming_error_2[..]);

        let extension = rust_jarm::extract_extension_info(input_packet, counter);

        assert_eq!(extension, expected_result);
    }
}