Implement fetch_user_info, separate xcode code, advance itunes implementation
Dadoum

Dadoum commited on 2026-01-06 02:58:02
Showing 17 changed files, with 744 additions and 275 deletions.

... ...
@@ -188,6 +188,7 @@ dependencies = [
188 188
  "sha2",
189 189
  "srp",
190 190
  "sys-locale",
191
+ "thiserror 2.0.17",
191 192
 ]
192 193
 
193 194
 [[package]]
... ...
@@ -282,6 +283,28 @@ dependencies = [
282 283
  "thiserror 1.0.69",
283 284
 ]
284 285
 
286
+[[package]]
287
+name = "aws-lc-rs"
288
+version = "1.15.2"
289
+source = "registry+https://github.com/rust-lang/crates.io-index"
290
+checksum = "6a88aab2464f1f25453baa7a07c84c5b7684e274054ba06817f382357f77a288"
291
+dependencies = [
292
+ "aws-lc-sys",
293
+ "zeroize",
294
+]
295
+
296
+[[package]]
297
+name = "aws-lc-sys"
298
+version = "0.35.0"
299
+source = "registry+https://github.com/rust-lang/crates.io-index"
300
+checksum = "b45afffdee1e7c9126814751f88dddc747f41d91da16c9551a0f1e8a11e788a1"
301
+dependencies = [
302
+ "cc",
303
+ "cmake",
304
+ "dunce",
305
+ "fs_extra",
306
+]
307
+
285 308
 [[package]]
286 309
 name = "backtrace"
287 310
 version = "0.3.76"
... ...
@@ -417,14 +440,22 @@ dependencies = [
417 440
 
418 441
 [[package]]
419 442
 name = "cc"
420
-version = "1.2.41"
443
+version = "1.2.51"
421 444
 source = "registry+https://github.com/rust-lang/crates.io-index"
422
-checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7"
445
+checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203"
423 446
 dependencies = [
424 447
  "find-msvc-tools",
448
+ "jobserver",
449
+ "libc",
425 450
  "shlex 1.3.0",
426 451
 ]
427 452
 
453
+[[package]]
454
+name = "cesu8"
455
+version = "1.1.0"
456
+source = "registry+https://github.com/rust-lang/crates.io-index"
457
+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
458
+
428 459
 [[package]]
429 460
 name = "cexpr"
430 461
 version = "0.4.0"
... ...
@@ -467,9 +498,9 @@ dependencies = [
467 498
 
468 499
 [[package]]
469 500
 name = "cipher"
470
-version = "0.5.0-rc.2"
501
+version = "0.5.0-rc.3"
471 502
 source = "registry+https://github.com/rust-lang/crates.io-index"
472
-checksum = "155e4a260750fa4f7754649f049748aacc31db238a358d85fd721002f230f92f"
503
+checksum = "98d708bac5451350d56398433b19a7889022fa9187df1a769c0edbc3b2c03167"
473 504
 dependencies = [
474 505
  "block-buffer",
475 506
  "crypto-common",
... ...
@@ -502,12 +533,31 @@ dependencies = [
502 533
  "vec_map",
503 534
 ]
504 535
 
536
+[[package]]
537
+name = "cmake"
538
+version = "0.1.57"
539
+source = "registry+https://github.com/rust-lang/crates.io-index"
540
+checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d"
541
+dependencies = [
542
+ "cc",
543
+]
544
+
505 545
 [[package]]
506 546
 name = "colorchoice"
507 547
 version = "1.0.4"
508 548
 source = "registry+https://github.com/rust-lang/crates.io-index"
509 549
 checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
510 550
 
551
+[[package]]
552
+name = "combine"
553
+version = "4.6.7"
554
+source = "registry+https://github.com/rust-lang/crates.io-index"
555
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
556
+dependencies = [
557
+ "bytes",
558
+ "memchr",
559
+]
560
+
511 561
 [[package]]
512 562
 name = "const-oid"
513 563
 version = "0.10.1"
... ...
@@ -524,6 +574,16 @@ dependencies = [
524 574
  "libc",
525 575
 ]
526 576
 
577
+[[package]]
578
+name = "core-foundation"
579
+version = "0.10.1"
580
+source = "registry+https://github.com/rust-lang/crates.io-index"
581
+checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
582
+dependencies = [
583
+ "core-foundation-sys",
584
+ "libc",
585
+]
586
+
527 587
 [[package]]
528 588
 name = "core-foundation-sys"
529 589
 version = "0.8.7"
... ...
@@ -614,9 +674,9 @@ dependencies = [
614 674
 
615 675
 [[package]]
616 676
 name = "digest"
617
-version = "0.11.0-rc.4"
677
+version = "0.11.0-rc.5"
618 678
 source = "registry+https://github.com/rust-lang/crates.io-index"
619
-checksum = "ea390c940e465846d64775e55e3115d5dc934acb953de6f6e6360bc232fe2bf7"
679
+checksum = "ebf9423bafb058e4142194330c52273c343f8a5beb7176d052f0e73b17dd35b9"
620 680
 dependencies = [
621 681
  "block-buffer",
622 682
  "const-oid",
... ...
@@ -635,6 +695,35 @@ dependencies = [
635 695
  "syn 2.0.107",
636 696
 ]
637 697
 
698
+[[package]]
699
+name = "dlopen2"
700
+version = "0.8.2"
701
+source = "registry+https://github.com/rust-lang/crates.io-index"
702
+checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4"
703
+dependencies = [
704
+ "dlopen2_derive",
705
+ "libc",
706
+ "once_cell",
707
+ "winapi",
708
+]
709
+
710
+[[package]]
711
+name = "dlopen2_derive"
712
+version = "0.4.3"
713
+source = "registry+https://github.com/rust-lang/crates.io-index"
714
+checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f"
715
+dependencies = [
716
+ "proc-macro2",
717
+ "quote",
718
+ "syn 2.0.107",
719
+]
720
+
721
+[[package]]
722
+name = "dunce"
723
+version = "1.0.5"
724
+source = "registry+https://github.com/rust-lang/crates.io-index"
725
+checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
726
+
638 727
 [[package]]
639 728
 name = "either"
640 729
 version = "1.15.0"
... ...
@@ -721,9 +810,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
721 810
 
722 811
 [[package]]
723 812
 name = "find-msvc-tools"
724
-version = "0.1.4"
813
+version = "0.1.6"
725 814
 source = "registry+https://github.com/rust-lang/crates.io-index"
726
-checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
815
+checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff"
727 816
 
728 817
 [[package]]
729 818
 name = "flate2"
... ...
@@ -765,6 +854,20 @@ dependencies = [
765 854
  "percent-encoding",
766 855
 ]
767 856
 
857
+[[package]]
858
+name = "fpsd-proxy"
859
+version = "0.1.0"
860
+dependencies = [
861
+ "dlopen2",
862
+ "objc2",
863
+]
864
+
865
+[[package]]
866
+name = "fs_extra"
867
+version = "1.3.0"
868
+source = "registry+https://github.com/rust-lang/crates.io-index"
869
+checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
870
+
768 871
 [[package]]
769 872
 name = "futures"
770 873
 version = "0.3.31"
... ...
@@ -1391,9 +1494,9 @@ dependencies = [
1391 1494
 
1392 1495
 [[package]]
1393 1496
 name = "inout"
1394
-version = "0.2.1"
1497
+version = "0.2.2"
1395 1498
 source = "registry+https://github.com/rust-lang/crates.io-index"
1396
-checksum = "c7357b6e7aa75618c7864ebd0634b115a7218b0615f4cb1df33ac3eca23943d4"
1499
+checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7"
1397 1500
 dependencies = [
1398 1501
  "block-padding",
1399 1502
  "hybrid-array",
... ...
@@ -1486,6 +1589,38 @@ dependencies = [
1486 1589
  "syn 2.0.107",
1487 1590
 ]
1488 1591
 
1592
+[[package]]
1593
+name = "jni"
1594
+version = "0.21.1"
1595
+source = "registry+https://github.com/rust-lang/crates.io-index"
1596
+checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
1597
+dependencies = [
1598
+ "cesu8",
1599
+ "cfg-if 1.0.4",
1600
+ "combine",
1601
+ "jni-sys",
1602
+ "log",
1603
+ "thiserror 1.0.69",
1604
+ "walkdir",
1605
+ "windows-sys 0.45.0",
1606
+]
1607
+
1608
+[[package]]
1609
+name = "jni-sys"
1610
+version = "0.3.0"
1611
+source = "registry+https://github.com/rust-lang/crates.io-index"
1612
+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
1613
+
1614
+[[package]]
1615
+name = "jobserver"
1616
+version = "0.1.34"
1617
+source = "registry+https://github.com/rust-lang/crates.io-index"
1618
+checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
1619
+dependencies = [
1620
+ "getrandom 0.3.4",
1621
+ "libc",
1622
+]
1623
+
1489 1624
 [[package]]
1490 1625
 name = "js-sys"
1491 1626
 version = "0.3.81"
... ...
@@ -1698,10 +1833,10 @@ dependencies = [
1698 1833
  "libc",
1699 1834
  "log",
1700 1835
  "openssl",
1701
- "openssl-probe",
1836
+ "openssl-probe 0.1.6",
1702 1837
  "openssl-sys",
1703 1838
  "schannel",
1704
- "security-framework",
1839
+ "security-framework 2.11.1",
1705 1840
  "security-framework-sys",
1706 1841
  "tempfile",
1707 1842
 ]
... ...
@@ -1761,6 +1896,21 @@ dependencies = [
1761 1896
  "autocfg",
1762 1897
 ]
1763 1898
 
1899
+[[package]]
1900
+name = "objc2"
1901
+version = "0.6.3"
1902
+source = "registry+https://github.com/rust-lang/crates.io-index"
1903
+checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05"
1904
+dependencies = [
1905
+ "objc2-encode",
1906
+]
1907
+
1908
+[[package]]
1909
+name = "objc2-encode"
1910
+version = "4.1.0"
1911
+source = "registry+https://github.com/rust-lang/crates.io-index"
1912
+checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
1913
+
1764 1914
 [[package]]
1765 1915
 name = "object"
1766 1916
 version = "0.37.3"
... ...
@@ -1814,6 +1964,12 @@ version = "0.1.6"
1814 1964
 source = "registry+https://github.com/rust-lang/crates.io-index"
1815 1965
 checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
1816 1966
 
1967
+[[package]]
1968
+name = "openssl-probe"
1969
+version = "0.2.0"
1970
+source = "registry+https://github.com/rust-lang/crates.io-index"
1971
+checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391"
1972
+
1817 1973
 [[package]]
1818 1974
 name = "openssl-sys"
1819 1975
 version = "0.9.111"
... ...
@@ -2068,6 +2224,7 @@ version = "0.11.13"
2068 2224
 source = "registry+https://github.com/rust-lang/crates.io-index"
2069 2225
 checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31"
2070 2226
 dependencies = [
2227
+ "aws-lc-rs",
2071 2228
  "bytes",
2072 2229
  "getrandom 0.3.4",
2073 2230
  "lru-slab",
... ...
@@ -2273,6 +2430,44 @@ dependencies = [
2273 2430
  "webpki-roots 1.0.4",
2274 2431
 ]
2275 2432
 
2433
+[[package]]
2434
+name = "reqwest"
2435
+version = "0.13.1"
2436
+source = "registry+https://github.com/rust-lang/crates.io-index"
2437
+checksum = "04e9018c9d814e5f30cc16a0f03271aeab3571e609612d9fe78c1aa8d11c2f62"
2438
+dependencies = [
2439
+ "base64 0.22.1",
2440
+ "bytes",
2441
+ "encoding_rs",
2442
+ "futures-core",
2443
+ "h2 0.4.12",
2444
+ "http 1.3.1",
2445
+ "http-body 1.0.1",
2446
+ "http-body-util",
2447
+ "hyper 1.7.0",
2448
+ "hyper-rustls 0.27.7",
2449
+ "hyper-util",
2450
+ "js-sys",
2451
+ "log",
2452
+ "mime",
2453
+ "percent-encoding",
2454
+ "pin-project-lite",
2455
+ "quinn",
2456
+ "rustls 0.23.33",
2457
+ "rustls-pki-types",
2458
+ "rustls-platform-verifier",
2459
+ "sync_wrapper 1.0.2",
2460
+ "tokio",
2461
+ "tokio-rustls 0.26.4",
2462
+ "tower",
2463
+ "tower-http",
2464
+ "tower-service",
2465
+ "url",
2466
+ "wasm-bindgen",
2467
+ "wasm-bindgen-futures",
2468
+ "web-sys",
2469
+]
2470
+
2276 2471
 [[package]]
2277 2472
 name = "ring"
2278 2473
 version = "0.17.14"
... ...
@@ -2336,6 +2531,7 @@ version = "0.23.33"
2336 2531
 source = "registry+https://github.com/rust-lang/crates.io-index"
2337 2532
 checksum = "751e04a496ca00bb97a5e043158d23d66b5aabf2e1d5aa2a0aaebb1aafe6f82c"
2338 2533
 dependencies = [
2534
+ "aws-lc-rs",
2339 2535
  "once_cell",
2340 2536
  "ring",
2341 2537
  "rustls-pki-types",
... ...
@@ -2344,6 +2540,18 @@ dependencies = [
2344 2540
  "zeroize",
2345 2541
 ]
2346 2542
 
2543
+[[package]]
2544
+name = "rustls-native-certs"
2545
+version = "0.8.3"
2546
+source = "registry+https://github.com/rust-lang/crates.io-index"
2547
+checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63"
2548
+dependencies = [
2549
+ "openssl-probe 0.2.0",
2550
+ "rustls-pki-types",
2551
+ "schannel",
2552
+ "security-framework 3.5.1",
2553
+]
2554
+
2347 2555
 [[package]]
2348 2556
 name = "rustls-pemfile"
2349 2557
 version = "1.0.4"
... ...
@@ -2363,6 +2571,33 @@ dependencies = [
2363 2571
  "zeroize",
2364 2572
 ]
2365 2573
 
2574
+[[package]]
2575
+name = "rustls-platform-verifier"
2576
+version = "0.6.2"
2577
+source = "registry+https://github.com/rust-lang/crates.io-index"
2578
+checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784"
2579
+dependencies = [
2580
+ "core-foundation 0.10.1",
2581
+ "core-foundation-sys",
2582
+ "jni",
2583
+ "log",
2584
+ "once_cell",
2585
+ "rustls 0.23.33",
2586
+ "rustls-native-certs",
2587
+ "rustls-platform-verifier-android",
2588
+ "rustls-webpki 0.103.7",
2589
+ "security-framework 3.5.1",
2590
+ "security-framework-sys",
2591
+ "webpki-root-certs",
2592
+ "windows-sys 0.61.2",
2593
+]
2594
+
2595
+[[package]]
2596
+name = "rustls-platform-verifier-android"
2597
+version = "0.1.1"
2598
+source = "registry+https://github.com/rust-lang/crates.io-index"
2599
+checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
2600
+
2366 2601
 [[package]]
2367 2602
 name = "rustls-webpki"
2368 2603
 version = "0.101.7"
... ...
@@ -2379,6 +2614,7 @@ version = "0.103.7"
2379 2614
 source = "registry+https://github.com/rust-lang/crates.io-index"
2380 2615
 checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf"
2381 2616
 dependencies = [
2617
+ "aws-lc-rs",
2382 2618
  "ring",
2383 2619
  "rustls-pki-types",
2384 2620
  "untrusted",
... ...
@@ -2396,6 +2632,15 @@ version = "1.0.20"
2396 2632
 source = "registry+https://github.com/rust-lang/crates.io-index"
2397 2633
 checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
2398 2634
 
2635
+[[package]]
2636
+name = "same-file"
2637
+version = "1.0.6"
2638
+source = "registry+https://github.com/rust-lang/crates.io-index"
2639
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
2640
+dependencies = [
2641
+ "winapi-util",
2642
+]
2643
+
2399 2644
 [[package]]
2400 2645
 name = "schannel"
2401 2646
 version = "0.1.28"
... ...
@@ -2442,7 +2687,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
2442 2687
 checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
2443 2688
 dependencies = [
2444 2689
  "bitflags 2.10.0",
2445
- "core-foundation",
2690
+ "core-foundation 0.9.4",
2691
+ "core-foundation-sys",
2692
+ "libc",
2693
+ "security-framework-sys",
2694
+]
2695
+
2696
+[[package]]
2697
+name = "security-framework"
2698
+version = "3.5.1"
2699
+source = "registry+https://github.com/rust-lang/crates.io-index"
2700
+checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef"
2701
+dependencies = [
2702
+ "bitflags 2.10.0",
2703
+ "core-foundation 0.10.1",
2446 2704
  "core-foundation-sys",
2447 2705
  "libc",
2448 2706
  "security-framework-sys",
... ...
@@ -2600,7 +2858,7 @@ dependencies = [
2600 2858
 [[package]]
2601 2859
 name = "srp"
2602 2860
 version = "0.7.0-pre.0"
2603
-source = "git+https://github.com/RustCrypto/PAKEs#5e629b72caf484a3cb4c2292594a0fde8aa32df7"
2861
+source = "git+https://github.com/RustCrypto/PAKEs#ce6ac89ba36ed72becdd62146ac5f52882ff9f12"
2604 2862
 dependencies = [
2605 2863
  "digest",
2606 2864
  "lazy_static",
... ...
@@ -2640,10 +2898,13 @@ dependencies = [
2640 2898
  "adid-proxy",
2641 2899
  "android-coreadi",
2642 2900
  "apple-account",
2901
+ "base64 0.22.1",
2643 2902
  "elf-loader",
2644 2903
  "env_logger 0.11.8",
2645 2904
  "libc",
2905
+ "reqwest 0.13.1",
2646 2906
  "tokio",
2907
+ "xcode",
2647 2908
 ]
2648 2909
 
2649 2910
 [[package]]
... ...
@@ -2738,7 +2999,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
2738 2999
 checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
2739 3000
 dependencies = [
2740 3001
  "bitflags 1.3.2",
2741
- "core-foundation",
3002
+ "core-foundation 0.9.4",
2742 3003
  "system-configuration-sys 0.5.0",
2743 3004
 ]
2744 3005
 
... ...
@@ -2749,7 +3010,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
2749 3010
 checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
2750 3011
 dependencies = [
2751 3012
  "bitflags 2.10.0",
2752
- "core-foundation",
3013
+ "core-foundation 0.9.4",
2753 3014
  "system-configuration-sys 0.6.0",
2754 3015
 ]
2755 3016
 
... ...
@@ -3157,6 +3418,16 @@ version = "0.9.5"
3157 3418
 source = "registry+https://github.com/rust-lang/crates.io-index"
3158 3419
 checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
3159 3420
 
3421
+[[package]]
3422
+name = "walkdir"
3423
+version = "2.5.0"
3424
+source = "registry+https://github.com/rust-lang/crates.io-index"
3425
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
3426
+dependencies = [
3427
+ "same-file",
3428
+ "winapi-util",
3429
+]
3430
+
3160 3431
 [[package]]
3161 3432
 name = "want"
3162 3433
 version = "0.3.1"
... ...
@@ -3273,6 +3544,15 @@ dependencies = [
3273 3544
  "wasm-bindgen",
3274 3545
 ]
3275 3546
 
3547
+[[package]]
3548
+name = "webpki-root-certs"
3549
+version = "1.0.4"
3550
+source = "registry+https://github.com/rust-lang/crates.io-index"
3551
+checksum = "ee3e3b5f5e80bc89f30ce8d0343bf4e5f12341c51f3e26cbeecbc7c85443e85b"
3552
+dependencies = [
3553
+ "rustls-pki-types",
3554
+]
3555
+
3276 3556
 [[package]]
3277 3557
 name = "webpki-roots"
3278 3558
 version = "0.25.4"
... ...
@@ -3404,6 +3684,15 @@ dependencies = [
3404 3684
  "windows-link 0.1.3",
3405 3685
 ]
3406 3686
 
3687
+[[package]]
3688
+name = "windows-sys"
3689
+version = "0.45.0"
3690
+source = "registry+https://github.com/rust-lang/crates.io-index"
3691
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
3692
+dependencies = [
3693
+ "windows-targets 0.42.2",
3694
+]
3695
+
3407 3696
 [[package]]
3408 3697
 name = "windows-sys"
3409 3698
 version = "0.48.0"
... ...
@@ -3440,6 +3729,21 @@ dependencies = [
3440 3729
  "windows-link 0.2.1",
3441 3730
 ]
3442 3731
 
3732
+[[package]]
3733
+name = "windows-targets"
3734
+version = "0.42.2"
3735
+source = "registry+https://github.com/rust-lang/crates.io-index"
3736
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
3737
+dependencies = [
3738
+ "windows_aarch64_gnullvm 0.42.2",
3739
+ "windows_aarch64_msvc 0.42.2",
3740
+ "windows_i686_gnu 0.42.2",
3741
+ "windows_i686_msvc 0.42.2",
3742
+ "windows_x86_64_gnu 0.42.2",
3743
+ "windows_x86_64_gnullvm 0.42.2",
3744
+ "windows_x86_64_msvc 0.42.2",
3745
+]
3746
+
3443 3747
 [[package]]
3444 3748
 name = "windows-targets"
3445 3749
 version = "0.48.5"
... ...
@@ -3488,6 +3792,12 @@ dependencies = [
3488 3792
  "windows_x86_64_msvc 0.53.1",
3489 3793
 ]
3490 3794
 
3795
+[[package]]
3796
+name = "windows_aarch64_gnullvm"
3797
+version = "0.42.2"
3798
+source = "registry+https://github.com/rust-lang/crates.io-index"
3799
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
3800
+
3491 3801
 [[package]]
3492 3802
 name = "windows_aarch64_gnullvm"
3493 3803
 version = "0.48.5"
... ...
@@ -3506,6 +3816,12 @@ version = "0.53.1"
3506 3816
 source = "registry+https://github.com/rust-lang/crates.io-index"
3507 3817
 checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
3508 3818
 
3819
+[[package]]
3820
+name = "windows_aarch64_msvc"
3821
+version = "0.42.2"
3822
+source = "registry+https://github.com/rust-lang/crates.io-index"
3823
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
3824
+
3509 3825
 [[package]]
3510 3826
 name = "windows_aarch64_msvc"
3511 3827
 version = "0.48.5"
... ...
@@ -3524,6 +3840,12 @@ version = "0.53.1"
3524 3840
 source = "registry+https://github.com/rust-lang/crates.io-index"
3525 3841
 checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
3526 3842
 
3843
+[[package]]
3844
+name = "windows_i686_gnu"
3845
+version = "0.42.2"
3846
+source = "registry+https://github.com/rust-lang/crates.io-index"
3847
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
3848
+
3527 3849
 [[package]]
3528 3850
 name = "windows_i686_gnu"
3529 3851
 version = "0.48.5"
... ...
@@ -3554,6 +3876,12 @@ version = "0.53.1"
3554 3876
 source = "registry+https://github.com/rust-lang/crates.io-index"
3555 3877
 checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
3556 3878
 
3879
+[[package]]
3880
+name = "windows_i686_msvc"
3881
+version = "0.42.2"
3882
+source = "registry+https://github.com/rust-lang/crates.io-index"
3883
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
3884
+
3557 3885
 [[package]]
3558 3886
 name = "windows_i686_msvc"
3559 3887
 version = "0.48.5"
... ...
@@ -3572,6 +3900,12 @@ version = "0.53.1"
3572 3900
 source = "registry+https://github.com/rust-lang/crates.io-index"
3573 3901
 checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
3574 3902
 
3903
+[[package]]
3904
+name = "windows_x86_64_gnu"
3905
+version = "0.42.2"
3906
+source = "registry+https://github.com/rust-lang/crates.io-index"
3907
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
3908
+
3575 3909
 [[package]]
3576 3910
 name = "windows_x86_64_gnu"
3577 3911
 version = "0.48.5"
... ...
@@ -3590,6 +3924,12 @@ version = "0.53.1"
3590 3924
 source = "registry+https://github.com/rust-lang/crates.io-index"
3591 3925
 checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
3592 3926
 
3927
+[[package]]
3928
+name = "windows_x86_64_gnullvm"
3929
+version = "0.42.2"
3930
+source = "registry+https://github.com/rust-lang/crates.io-index"
3931
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
3932
+
3593 3933
 [[package]]
3594 3934
 name = "windows_x86_64_gnullvm"
3595 3935
 version = "0.48.5"
... ...
@@ -3608,6 +3948,12 @@ version = "0.53.1"
3608 3948
 source = "registry+https://github.com/rust-lang/crates.io-index"
3609 3949
 checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
3610 3950
 
3951
+[[package]]
3952
+name = "windows_x86_64_msvc"
3953
+version = "0.42.2"
3954
+source = "registry+https://github.com/rust-lang/crates.io-index"
3955
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
3956
+
3611 3957
 [[package]]
3612 3958
 name = "windows_x86_64_msvc"
3613 3959
 version = "0.48.5"
... ...
@@ -3648,6 +3994,17 @@ version = "0.6.1"
3648 3994
 source = "registry+https://github.com/rust-lang/crates.io-index"
3649 3995
 checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
3650 3996
 
3997
+[[package]]
3998
+name = "xcode"
3999
+version = "0.1.0"
4000
+dependencies = [
4001
+ "adi",
4002
+ "apple-account",
4003
+ "plist",
4004
+ "plist-macros",
4005
+ "reqwest 0.12.27",
4006
+]
4007
+
3651 4008
 [[package]]
3652 4009
 name = "xpc-connection-sys"
3653 4010
 version = "0.1.1"
... ...
@@ -1,6 +1,6 @@
1 1
 [workspace]
2 2
 resolver = "2"
3
-members = ["adi", "adid-proxy", "android-coreadi", "apple-account", "apple-codesign-quick", "elf-loader", "plist-macros", "purple-builder", "super-sideloader"]
3
+members = ["adi", "adid-proxy", "android-coreadi", "apple-account", "apple-codesign-quick", "elf-loader", "fpsd-proxy", "plist-macros", "purple-builder", "super-sideloader", "xcode"]
4 4
 
5 5
 [workspace.dependencies]
6 6
 adi = { path = "adi" }
... ...
@@ -9,9 +9,11 @@ android-coreadi = { path = "android-coreadi" }
9 9
 apple-account = { path = "apple-account" }
10 10
 elf-loader = { path = "elf-loader" }
11 11
 plist-macros = { path = "plist-macros" }
12
+xcode = { path = "xcode" }
12 13
 
13 14
 plist = "1"
14 15
 reqwest = { version = "0.12", features = ["rustls-tls"] }
16
+thiserror = "2"
15 17
 tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
16 18
 
17 19
 aes = "0.9.0-rc.2"
... ...
@@ -9,6 +9,7 @@ plist.workspace = true
9 9
 plist-macros.workspace = true
10 10
 
11 11
 reqwest.workspace = true
12
+thiserror.workspace = true
12 13
 
13 14
 aes.workspace = true
14 15
 aes-gcm.workspace = true
... ...
@@ -1,4 +1,5 @@
1
-pub struct BundleInformation<'lt> { // Maybe simplify to 'static?
1
+pub struct BundleInformation<'lt> {
2
+    // Maybe simplify to 'static?
2 3
     pub bundle_name: &'lt str,
3 4
     pub bundle_identifier: &'lt str,
4 5
     pub bundle_version: &'lt str,
... ...
@@ -11,13 +12,6 @@ pub const AUTH_KIT_BUNDLE_INFORMATION: BundleInformation = BundleInformation {
11 12
     bundle_version: "1",
12 13
 };
13 14
 
14
-/// Xcode 16.4
15
-pub const XCODE_BUNDLE_INFORMATION: BundleInformation = BundleInformation {
16
-    bundle_name: "Xcode",
17
-    bundle_identifier: "com.apple.dt.Xcode",
18
-    bundle_version: "23792",
19
-};
20
-
21 15
 /// Apple TV 1.5.6 (macOS Sequoia 15.7.3)
22 16
 pub const APPLE_TV_BUNDLE_INFORMATION: BundleInformation = BundleInformation {
23 17
     bundle_name: "TV",
... ...
@@ -1,10 +1,9 @@
1 1
 mod anisette;
2
+mod authenticated_session;
2 3
 mod post_data;
3 4
 mod secondary_actions;
4 5
 mod url_switch;
5
-mod authenticated_session;
6 6
 
7
-pub use authenticated_session::*;
8 7
 use crate::bundle_information::BundleInformation;
9 8
 use crate::device::Device;
10 9
 use crate::grandslam::AuthOutcome::AnisetteResyncRequired;
... ...
@@ -12,31 +11,29 @@ use crate::http_session::{
12 11
     AnisetteHTTPSession, AppleError, BasicHTTPSession, HTTPSession, HTTPSessionCreationError,
13 12
     URLBagError, parse_status,
14 13
 };
14
+use crate::plist_request::dict_to_body;
15 15
 use adi::proxy::{ADIError, ADIResult};
16
-use aes::Aes256;
17 16
 use aes::cipher::block_padding;
18 17
 use aes::cipher::block_padding::Pkcs7;
19
-use aes_gcm::AesGcm;
20
-use aes_gcm::aead::consts::U16;
21
-use aes_gcm::aead::{Aead, Payload};
22 18
 pub use anisette::*;
19
+pub use authenticated_session::*;
23 20
 use base64::Engine;
24 21
 use base64::prelude::BASE64_STANDARD;
25 22
 use bytes::Bytes;
26 23
 use cbc::cipher::{BlockModeDecrypt, KeyIvInit};
27 24
 use chrono::{Local, SecondsFormat};
28 25
 use hmac::{Hmac, KeyInit, Mac};
29
-use log::{trace, warn};
26
+use log::trace;
30 27
 use plist::{Dictionary, Value};
31 28
 use plist_macros::{array, dict};
32 29
 pub use post_data::*;
33 30
 use reqwest::{Certificate, Method, RequestBuilder};
34
-use serde::Deserialize;
35 31
 use sha2::{Digest, Sha256};
36 32
 use srp::client::SrpClient;
37 33
 use srp::groups::G_2048;
38 34
 use srp::types::SrpAuthError;
39 35
 use std::fmt::{Display, Formatter};
36
+use thiserror::Error;
40 37
 pub use url_switch::*;
41 38
 
42 39
 const ROOT_CA: &[u8] = include_bytes!("grandslam/root-ca.pem");
... ...
@@ -184,89 +181,35 @@ impl<'lt, 'lt2, 'adi> Token<'lt, 'lt2, 'adi> {
184 181
 #[derive(Debug)]
185 182
 pub enum AuthOutcome {
186 183
     Success(Dictionary),
187
-    SecondaryActionRequired(Dictionary, String),
184
+    SecondaryActionRequired(Option<Dictionary>, String),
188 185
     AnisetteResyncRequired(Vec<u8>),
189 186
     AnisetteReprovisionRequired,
190 187
     UrlSwitchingRequired(String),
191 188
 }
192 189
 
193
-#[derive(Debug)]
190
+#[derive(Error, Debug)]
194 191
 pub enum AuthError {
192
+    #[error("Could not log-in to Apple servers: {0}")]
193
+    Apple(#[from] AppleError),
194
+    #[error("Cannot generate device authentication data: {0}")]
195
+    Anisette(#[from] ADIError),
196
+    #[error("Network error: {0}")]
197
+    Network(#[from] reqwest::Error),
198
+    // vvv Internal errors vvv
199
+    #[error("Invalid URL bag")]
195 200
     InvalidURLBag,
196
-    Apple(AppleError),
197
-    Anisette(ADIError),
198
-    Network(reqwest::Error),
199
-    Parsing(u8, plist::Error),
201
+    #[error("Cannot parse server response from step {auth_step}: {error}")]
202
+    Parsing { auth_step: u8, error: plist::Error },
203
+    #[error("Invalid server response structure (censor private data before reporting): {0:?}")]
200 204
     Structure(Dictionary),
201
-    SRP(SrpAuthError),
205
+    #[error("Authentication protocol failure: {0}")]
206
+    SRP(#[from] SrpAuthError),
207
+    #[error("Unknown server protocol: {0}")]
202 208
     UnknownProtocol(String),
203
-    Decryption(block_padding::Error),
204
-}
205
-
206
-/*
207
-impl From<SrpAuthError> for AuthError {
208
-    fn from(error: SrpAuthError) -> Self {
209
-        AuthError::SRP(error)
210
-    }
211
-}
212
-
213
-impl From<reqwest::Error> for AuthError {
214
-    fn from(error: reqwest::Error) -> Self {
215
-        Self::Network(error)
216
-    }
209
+    #[error("Decryption error: {0}")]
210
+    Decryption(#[from] block_padding::Error),
217 211
 }
218 212
 
219
-impl From<ADIError> for AuthError {
220
-    fn from(err: ADIError) -> AuthError {
221
-        AuthError::Anisette(err)
222
-    }
223
-}
224
-
225
-impl From<AppleError> for AuthError {
226
-    fn from(err: AppleError) -> AuthError {
227
-        AuthError::Apple(err)
228
-    }
229
-} // */
230
-
231
-impl Display for AuthError {
232
-    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
233
-        match self {
234
-            AuthError::InvalidURLBag => {
235
-                write!(f, "Invalid URL bag")
236
-            }
237
-            AuthError::Apple(err) => {
238
-                write!(f, "Could not log-in to Apple servers: {err}")
239
-            }
240
-            AuthError::Anisette(err) => {
241
-                write!(f, "Cannot generate device authentication data: {err}")
242
-            }
243
-            AuthError::Network(err) => {
244
-                write!(f, "Network error: {err}")
245
-            }
246
-            AuthError::Parsing(step, err) => {
247
-                write!(f, "Cannot parse server response from step {step}: {err}")
248
-            }
249
-            AuthError::Structure(dict) => {
250
-                write!(
251
-                    f,
252
-                    "Invalid server response structure (censor private data before reporting): {dict:?}"
253
-                )
254
-            }
255
-            AuthError::SRP(error) => {
256
-                write!(f, "Authentication protocol failure: {error}")
257
-            }
258
-            AuthError::UnknownProtocol(protocol) => {
259
-                write!(f, "Unknown server protocol: {protocol}")
260
-            }
261
-            AuthError::Decryption(block_padding) => {
262
-                write!(f, "Decryption error: {block_padding}")
263
-            }
264
-        }
265
-    }
266
-}
267
-
268
-impl std::error::Error for AuthError {}
269
-
270 213
 pub type AuthResult = Result<AuthOutcome, AuthError>;
271 214
 
272 215
 pub fn build_client_provided_data(
... ...
@@ -357,8 +300,9 @@ pub async fn login(
357 300
         .and_then(Value::as_string)
358 301
         .ok_or(AuthError::InvalidURLBag)?;
359 302
 
360
-    let cpd = build_client_provided_data(http_session).map_err(AuthError::Anisette)?;
303
+    let cpd = build_client_provided_data(http_session)?;
361 304
 
305
+    // TODO: implement s4k, if some day someone needs that.
362 306
     let srp_client = SrpClient::<Sha256>::new_with_options(&G_2048, true);
363 307
     let a: [u8; 256] = rand::random();
364 308
     let a_pub = srp_client.compute_public_ephemeral(&a);
... ...
@@ -379,23 +323,19 @@ pub async fn login(
379 323
         }
380 324
     };
381 325
 
382
-    // TODO: make a function to easily send plist.
383
-    let mut request_body = Vec::new();
384
-    plist::to_writer_xml(&mut request_body, &request_plist).unwrap();
385
-
386 326
     let response = http_session
387
-        .anisette_request_builder(GRANDSLAM_DSID, Method::POST, gs_service_url)
388
-        .map_err(AuthError::Anisette)?
389
-        .body(request_body)
327
+        .anisette_request_builder(GRANDSLAM_DSID, Method::POST, gs_service_url)?
328
+        .body(dict_to_body(request_plist))
390 329
         .send()
391
-        .await
392
-        .map_err(AuthError::Network)?
330
+        .await?
393 331
         .bytes()
394
-        .await
395
-        .map_err(AuthError::Network)?;
332
+        .await?;
396 333
 
397 334
     let response_plist: Dictionary =
398
-        plist::from_bytes(&response).map_err(|err| AuthError::Parsing(1, err))?;
335
+        plist::from_bytes(&response).map_err(|error| AuthError::Parsing {
336
+            auth_step: 0,
337
+            error,
338
+        })?;
399 339
 
400 340
     let response_dict = response_plist
401 341
         .get("Response")
... ...
@@ -435,7 +375,9 @@ pub async fn login(
435 375
         .ok_or(AuthError::Structure(response_plist.clone()))?;
436 376
 
437 377
     let hashed_password: Vec<u8> = match selected_protocol {
438
-        "s2k" => Sha256::digest(password.as_bytes()).to_vec(),
378
+        // SRP with a 2048/4096-bit long A.
379
+        "s2k" | "s4k" => Sha256::digest(password.as_bytes()).to_vec(),
380
+        // SRP with a 2048-bit long A + fo?
439 381
         "s2k_fo" => hex::encode(Sha256::digest(password.as_bytes())).into_bytes(),
440 382
         _ => {
441 383
             return Err(AuthError::UnknownProtocol(selected_protocol.into()));
... ...
@@ -449,11 +391,10 @@ pub async fn login(
449 391
         buf
450 392
     };
451 393
 
452
-    let verifier = srp_client
453
-        .process_reply_rfc5054(&a, apple_id.as_bytes(), &processed_password, salt, b)
454
-        .map_err(AuthError::SRP)?;
394
+    let verifier =
395
+        srp_client.process_reply_rfc5054(&a, apple_id.as_bytes(), &processed_password, salt, b)?;
455 396
 
456
-    let cpd = build_client_provided_data(http_session).map_err(AuthError::Anisette)?;
397
+    let cpd = build_client_provided_data(http_session)?;
457 398
 
458 399
     let request_plist = dict! {
459 400
         "Header": dict!{
... ...
@@ -468,22 +409,19 @@ pub async fn login(
468 409
         }
469 410
     };
470 411
 
471
-    let mut request_body = Vec::new();
472
-    plist::to_writer_xml(&mut request_body, &request_plist).unwrap();
473
-
474 412
     let response = http_session
475
-        .anisette_request_builder(GRANDSLAM_DSID, Method::POST, gs_service_url)
476
-        .map_err(AuthError::Anisette)?
477
-        .body(request_body)
413
+        .anisette_request_builder(GRANDSLAM_DSID, Method::POST, gs_service_url)?
414
+        .body(dict_to_body(request_plist))
478 415
         .send()
479
-        .await
480
-        .map_err(AuthError::Network)?
416
+        .await?
481 417
         .bytes()
482
-        .await
483
-        .map_err(AuthError::Network)?;
418
+        .await?;
484 419
 
485 420
     let response_plist: Dictionary =
486
-        plist::from_bytes(&response).map_err(|err| AuthError::Parsing(2, err))?;
421
+        plist::from_bytes(&response).map_err(|error| AuthError::Parsing {
422
+            auth_step: 1,
423
+            error,
424
+        })?;
487 425
 
488 426
     trace!("Received: {:#?}", response_plist);
489 427
 
... ...
@@ -499,7 +437,7 @@ pub async fn login(
499 437
         .and_then(|status| status.as_dictionary())
500 438
         .ok_or(AuthError::Structure(response_plist.clone()))?;
501 439
 
502
-    parse_status(status).map_err(AuthError::Apple)?;
440
+    parse_status(status)?;
503 441
 
504 442
     let status_code: StatusCode = status
505 443
         .get("hsc")
... ...
@@ -511,7 +449,7 @@ pub async fn login(
511 449
     let server_provided_data = response_dict
512 450
         .get("spd")
513 451
         .and_then(|server_provided_data| server_provided_data.as_data())
514
-        .map(|server_provided_data| {
452
+        .map::<Result<_, AuthError>, _>(|server_provided_data| {
515 453
             let server_reply = response_dict
516 454
                 .get("M2")
517 455
                 .and_then(|server_reply| server_reply.as_data())
... ...
@@ -546,7 +484,10 @@ pub async fn login(
546 484
             .map_err(AuthError::Decryption)?;
547 485
 
548 486
             let server_provided_data: Dictionary = plist::from_bytes(&server_provided_data)
549
-                .map_err(|err| AuthError::Parsing(2, err))?;
487
+                .map_err(|error| AuthError::Parsing {
488
+                    auth_step: 3,
489
+                    error,
490
+                })?;
550 491
 
551 492
             trace!("Parsed server provided data: {:#?}", server_provided_data);
552 493
 
... ...
@@ -556,7 +497,8 @@ pub async fn login(
556 497
 
557 498
     match status_code {
558 499
         StatusCode::Success => {
559
-            let server_provided_data = server_provided_data.unwrap_or_default();
500
+            let server_provided_data =
501
+                server_provided_data.expect("No server data has been provided??");
560 502
             Ok(AuthOutcome::Success(server_provided_data))
561 503
         }
562 504
         StatusCode::SecondaryActionRequired => {
... ...
@@ -566,8 +508,6 @@ pub async fn login(
566 508
                 .map(|action_url| action_url.to_string())
567 509
                 .ok_or(AuthError::Structure(response_plist))?;
568 510
 
569
-            let server_provided_data = server_provided_data.unwrap_or_default();
570
-
571 511
             Ok(AuthOutcome::SecondaryActionRequired(
572 512
                 server_provided_data,
573 513
                 action_url,
... ...
@@ -11,14 +11,14 @@ use std::fmt::{Display, Formatter};
11 11
 #[derive(Debug)]
12 12
 pub enum ProvisioningError {
13 13
     ADIError(ADIError),
14
+    AppleError(AppleError),
15
+    URLNotFound(String),
14 16
     StartProvisioningRequest(reqwest::Error),
15 17
     StartProvisioningRequestPlist(plist::Error),
16 18
     StartProvisioningRequestStructure(Dictionary),
17
-    AppleError(AppleError),
18 19
     FinishProvisioningRequest(reqwest::Error),
19 20
     FinishProvisioningRequestPlist(plist::Error),
20 21
     FinishProvisioningRequestStructure(Dictionary),
21
-    URLNotFound(String),
22 22
 }
23 23
 
24 24
 impl Display for ProvisioningError {
... ...
@@ -1,3 +1,6 @@
1
+use crate::grandslam::{GRANDSLAM_DSID, build_client_provided_data};
2
+use crate::http_session::{AnisetteHTTPSession, AppleError, URLBag, parse_status};
3
+use adi::proxy::{ADIError, ADIResult};
1 4
 use aes::Aes256;
2 5
 use aes::cipher::consts::U16;
3 6
 use aes_gcm::aead::{Aead, Payload};
... ...
@@ -7,16 +10,14 @@ use base64::prelude::BASE64_STANDARD;
7 10
 use hmac::{Hmac, Mac};
8 11
 use log::{trace, warn};
9 12
 use plist::{Dictionary, Value};
13
+use plist_macros::{array, dict};
10 14
 use reqwest::{Method, RequestBuilder};
11 15
 use serde::Deserialize;
12 16
 use sha2::Sha256;
13
-use adi::proxy::{ADIError, ADIResult};
14
-use plist_macros::{array, dict};
15
-use crate::grandslam::{build_client_provided_data, GRANDSLAM_DSID};
16
-use crate::http_session::{parse_status, AnisetteHTTPSession, AppleError, URLBag};
17
+use thiserror::Error;
17 18
 
18
-#[derive(Debug, Deserialize)]
19
-pub struct GSToken {
19
+#[derive(Debug, Deserialize, Clone)]
20
+pub struct Token {
20 21
     pub duration: u64,
21 22
     // #[serde(rename = "cts")]
22 23
     // pub start_epoch_millis: u64,
... ...
@@ -49,7 +50,7 @@ impl AuthToken {
49 50
 
50 51
 pub fn parse_tokens_from_server_provided_data(
51 52
     server_provided_data: &Dictionary,
52
-) -> Option<(AuthToken, Vec<(String, GSToken)>)> {
53
+) -> Option<(AuthToken, Vec<(String, Token)>)> {
53 54
     let alt_dsid = server_provided_data
54 55
         .get("adsid")
55 56
         .and_then(|alt_dsid| alt_dsid.as_string())
... ...
@@ -86,85 +87,116 @@ pub fn parse_tokens_from_server_provided_data(
86 87
 
87 88
     match (alt_dsid, idms_token, session_key, cookie) {
88 89
         (Some(alt_dsid), Some(idms_token), Some(session_key), Some(cookie)) => Some((
89
-            AuthToken::new(
90
-                alt_dsid,
91
-                idms_token,
92
-                session_key,
93
-                cookie,
94
-            ),
90
+            AuthToken::new(alt_dsid, idms_token, session_key, cookie),
95 91
             tokens.unwrap_or_default(),
96 92
         )),
97 93
         _ => None,
98 94
     }
99 95
 }
100 96
 
101
-#[derive(Debug)]
97
+#[derive(Debug, Error)]
102 98
 pub enum AppTokenRequestError {
103
-    InvalidURLBag,
104
-    Anisette(ADIError),
99
+    #[error("Cannot proceed: {0}")]
100
+    Apple(#[from] AppleError),
101
+    #[error("Cannot generate device authentication data: {0}")]
102
+    Anisette(#[from] ADIError),
103
+    #[error("The provided authentication token is not valid")]
105 104
     InvalidAuthToken,
106
-    Network(reqwest::Error),
107
-    Parsing(plist::Error),
105
+    // vvvv Internal errors vvvv
106
+    #[error("Invalid URL bag")]
107
+    InvalidURLBag,
108
+    #[error("Network error: {0}")]
109
+    Network(#[from] reqwest::Error),
110
+    #[error("Failed to parse the app token request response: {0}")]
111
+    Parsing(#[from] plist::Error),
112
+    #[error("Failed to parse the app token request response")]
108 113
     Structure(Dictionary),
109
-    Apple(AppleError),
114
+    #[error("Invalid token returned by the server")]
110 115
     InvalidResponse,
111 116
 }
112 117
 
113
-impl std::fmt::Display for AppTokenRequestError {
114
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
115
-        match self {
116
-            AppTokenRequestError::InvalidURLBag => write!(f, "Invalid URL bag"),
117
-            AppTokenRequestError::Anisette(err) => {
118
-                write!(f, "Cannot generate device authentication data: {err}")
119
-            }
120
-            AppTokenRequestError::InvalidAuthToken => {
121
-                write!(f, "The provided authentication token is not valid")
122
-            }
123
-            AppTokenRequestError::Network(err) => write!(f, "Network error: {err}"),
124
-            AppTokenRequestError::Parsing(err) => {
125
-                write!(f, "Failed to parse the app token request response: {err}")
126
-            }
127
-            AppTokenRequestError::Structure(_) => {
128
-                write!(f, "Failed to parse the app token request response")
129
-            }
130
-            AppTokenRequestError::Apple(err) => write!(f, "Cannot proceed: {err}"),
131
-            AppTokenRequestError::InvalidResponse => {
132
-                write!(f, "Invalid token returned by the server")
133
-            }
134
-        }
135
-    }
118
+pub struct AppTokenIdentifier<'lt>(pub &'lt str);
119
+
120
+#[derive(Debug, Error)]
121
+pub enum AuthenticatedRequestError {
122
+    #[error("Server returned: {0}")]
123
+    Apple(#[from] AppleError),
124
+    #[error("Cannot generate Anisette headers: {0}")]
125
+    Anisette(#[from] ADIError),
126
+    #[error("Invalid URL bag")]
127
+    InvalidURLBag,
128
+    #[error("Network error: {0}")]
129
+    Network(#[from] reqwest::Error),
130
+    #[error("Invalid server response")]
131
+    InvalidResponse(plist::Error),
136 132
 }
137 133
 
138
-impl std::error::Error for AppTokenRequestError {}
134
+pub type AuthenticatedRequestResult<T> = Result<T, AuthenticatedRequestError>;
139 135
 
140 136
 pub struct AuthenticatedHTTPSession<'lt, 'adi> {
141 137
     pub http_session: AnisetteHTTPSession<'lt, 'adi>,
142 138
     pub auth_token: AuthToken,
139
+    pub hb_token: String,
143 140
 }
144 141
 
145 142
 impl<'a, 'b> AuthenticatedHTTPSession<'a, 'b> {
146
-    pub fn new(http_session: AnisetteHTTPSession<'a, 'b>, auth_token: AuthToken) -> Self {
143
+    pub fn new(
144
+        http_session: AnisetteHTTPSession<'a, 'b>,
145
+        auth_token: AuthToken,
146
+        heartbeat_token: Token,
147
+    ) -> Self {
148
+        let hb_token =
149
+            BASE64_STANDARD.encode(format!("{}:{}", auth_token.alt_dsid, heartbeat_token.token));
150
+
147 151
         Self {
148 152
             http_session,
149 153
             auth_token,
154
+            hb_token,
150 155
         }
151 156
     }
152 157
 
153
-    pub fn url_bag(&self) -> &URLBag { self.http_session.url_bag() }
154
-    pub fn simple_request_builder(&self, method: Method, url: &str) -> RequestBuilder { self.http_session.simple_request_builder(method, url) }
155
-    pub fn anisette_request_builder(&self, method: Method, url: &str) -> ADIResult<RequestBuilder> { self.http_session.anisette_request_builder(GRANDSLAM_DSID, method, url) }
156
-    pub fn authenticated_request_builder(&self, method: Method, url: &str) -> ADIResult<RequestBuilder> {
157
-        Ok(
158
-            self.anisette_request_builder(method, url)?
159
-                .header("X-Apple-Identity-Token", self.auth_token.identity_token.as_str())
160
-        )
158
+    pub fn url_bag(&self) -> &URLBag {
159
+        self.http_session.url_bag()
160
+    }
161
+
162
+    pub fn simple_request_builder(&self, method: Method, url: &str) -> RequestBuilder {
163
+        self.http_session.simple_request_builder(method, url)
164
+    }
165
+
166
+    pub fn anisette_request_builder(&self, method: Method, url: &str) -> ADIResult<RequestBuilder> {
167
+        self.http_session
168
+            .anisette_request_builder(GRANDSLAM_DSID, method, url)
169
+    }
170
+
171
+    pub fn authenticated_request_builder(
172
+        &self,
173
+        method: Method,
174
+        url: &str,
175
+    ) -> ADIResult<RequestBuilder> {
176
+        self.anisette_request_builder(method, url).map(|builder| {
177
+            builder
178
+                // .header(
179
+                //     "X-Apple-Identity-Token",
180
+                //     self.auth_token.identity_token.as_str(),
181
+                // )
182
+                // .header("X-Apple-I-Identity-Id", self.auth_token.alt_dsid.as_str())
183
+                // .header("X-Apple-DSID", self.auth_token.alt_dsid.as_str())
184
+                // .header("X-Apple-GS-Token", self.auth_token.idms_token.as_str())
185
+                // .header("X-Apple-I-CK-Presence", "false")
186
+                .header("X-Apple-HB-Token", self.hb_token.as_str())
187
+        })
161 188
     }
162 189
 
163 190
     pub async fn get_app_token(
164 191
         &self,
165
-        app_token_identifier: &str, // TODO: maybe create some type for app tokens identifier?
166
-    ) -> Result<GSToken, AppTokenRequestError> {
167
-        let AuthenticatedHTTPSession { http_session, auth_token } = self;
192
+        app_token_identifier: AppTokenIdentifier<'_>,
193
+    ) -> Result<Token, AppTokenRequestError> {
194
+        let app_token_identifier = app_token_identifier.0;
195
+        let AuthenticatedHTTPSession {
196
+            http_session,
197
+            auth_token,
198
+            ..
199
+        } = self;
168 200
 
169 201
         let gs_service_url = http_session
170 202
             .url_bag()
... ...
@@ -172,7 +204,8 @@ impl<'a, 'b> AuthenticatedHTTPSession<'a, 'b> {
172 204
             .and_then(Value::as_string)
173 205
             .ok_or(AppTokenRequestError::InvalidURLBag)?;
174 206
 
175
-        let cpd = build_client_provided_data(&http_session).map_err(AppTokenRequestError::Anisette)?;
207
+        let cpd =
208
+            build_client_provided_data(&http_session).map_err(AppTokenRequestError::Anisette)?;
176 209
 
177 210
         let checksum = Hmac::<Sha256>::new_from_slice(&auth_token.session_key)
178 211
             .map_err(|_| AppTokenRequestError::InvalidAuthToken)?
... ...
@@ -229,7 +262,7 @@ impl<'a, 'b> AuthenticatedHTTPSession<'a, 'b> {
229 262
             .and_then(|status| status.as_dictionary())
230 263
             .ok_or(AppTokenRequestError::Structure(response_plist.clone()))?;
231 264
 
232
-        parse_status(status).map_err(AppTokenRequestError::Apple)?;
265
+        parse_status(status)?;
233 266
 
234 267
         let encrypted_tokens = response_dict
235 268
             .get("et")
... ...
@@ -270,16 +303,37 @@ impl<'a, 'b> AuthenticatedHTTPSession<'a, 'b> {
270 303
             .ok_or(AppTokenRequestError::InvalidResponse)
271 304
     }
272 305
 
273
-    pub async fn validate_code(&self, validation_code: &str) -> Result<(), AppTokenRequestError> { // TODO: change that error type.
306
+    pub async fn fetch_user_info(&self) -> AuthenticatedRequestResult<Dictionary> {
307
+        let url = self
308
+            .http_session
309
+            .url_bag()
310
+            .get("fetchUserInfo")
311
+            .and_then(Value::as_string)
312
+            .ok_or(AuthenticatedRequestError::InvalidURLBag)?;
313
+
314
+        let response = self
315
+            .authenticated_request_builder(Method::GET, url)?
316
+            .send()
317
+            .await?
318
+            .bytes()
319
+            .await?;
320
+
321
+        Ok(plist::from_bytes(&response).map_err(AuthenticatedRequestError::InvalidResponse)?)
322
+    }
323
+
324
+    pub async fn validate_code(
325
+        &self,
326
+        validation_code: &str,
327
+    ) -> Result<(), Box<dyn std::error::Error>> {
328
+        // TODO: change that error type.
274 329
         let validate_code_url = self
275 330
             .url_bag()
276 331
             .get("validateCode")
277 332
             .and_then(Value::as_string)
278 333
             .ok_or(AppTokenRequestError::InvalidURLBag)?;
279 334
 
280
-        let _ =
281
-            self.authenticated_request_builder(Method::POST, validate_code_url)
282
-                .map_err(AppTokenRequestError::Anisette)?
335
+        let _ = self
336
+            .authenticated_request_builder(Method::POST, validate_code_url)?
283 337
             .header("security-code", validation_code);
284 338
 
285 339
         todo!()
... ...
@@ -116,8 +116,10 @@ impl<'lt> BasicHTTPSession<'lt> {
116 116
             let mut headers = HeaderMap::new();
117 117
             headers.insert(
118 118
                 "User-Agent",
119
-                client_bundle_info
120
-                    .bundle_name
119
+                format!(
120
+                    "{}/{}",
121
+                    client_bundle_info.bundle_name, client_bundle_info.bundle_version
122
+                )
121 123
                 .parse()
122 124
                 .map_err(HTTPSessionCreationError::InvalidHeader)?,
123 125
             );
... ...
@@ -5,11 +5,13 @@ use crate::http_session::{
5 5
     URLBagError,
6 6
 };
7 7
 use adi::proxy::ADIError;
8
+use base64::Engine;
9
+use base64::prelude::BASE64_STANDARD;
8 10
 use bytes::Bytes;
9 11
 use plist::{Dictionary, Value};
10 12
 use plist_macros::dict;
11 13
 use reqwest::Method;
12
-use std::fmt::{Display, Formatter};
14
+use thiserror::Error;
13 15
 
14 16
 pub async fn http_session<'lt>(
15 17
     device: Device,
... ...
@@ -17,9 +19,11 @@ pub async fn http_session<'lt>(
17 19
 ) -> Result<HTTPSession<'lt>, HTTPSessionCreationError> {
18 20
     let http_session = BasicHTTPSession::new(device, client_bundle_info, None)?;
19 21
 
22
+    // https://init.itunes.apple.com/WebObjects/MZInit.woa/wa/initiateSession
20 23
     let url_bag_response = http_session
21
-        .request_builder(Method::GET, "https://sandbox.itunes.apple.com/bag.xml")
22
-        .query(&[("ix", 6)])
24
+        .request_builder(Method::GET, "https://init.itunes.apple.com/bag.xml")
25
+        .query(&[("ix", "6")])
26
+        .header("Content-Type", "application/x-apple-plist")
23 27
         .send()
24 28
         .await
25 29
         .map_err(HTTPSessionCreationError::Reqwest)?
... ...
@@ -32,6 +36,8 @@ pub async fn http_session<'lt>(
32 36
     let url_bag = parse_url_bag_response(url_bag_response)
33 37
         .map_err(|_| HTTPSessionCreationError::InvalidURLBagResponseStructure)?;
34 38
 
39
+    println!("url_bag: {:#?}", url_bag);
40
+
35 41
     HTTPSession::new(http_session, url_bag)
36 42
 }
37 43
 
... ...
@@ -42,39 +48,64 @@ pub(crate) fn parse_url_bag_response(url_bag_response: Bytes) -> Result<Dictiona
42 48
     Ok(url_bag_plist)
43 49
 }
44 50
 
45
-#[derive(Debug)]
51
+pub async fn setup_sap(http_session: &HTTPSession<'_>) -> Result<(), Box<dyn std::error::Error>> {
52
+    // TODO: error type
53
+    let sign_sap_setup_url = http_session
54
+        .url_bag()
55
+        .get("sign-sap-setup")
56
+        .and_then(Value::as_string)
57
+        .ok_or(AuthError::InvalidURLBag)?;
58
+
59
+    let sap_certificate_response = http_session
60
+        .request_builder(Method::GET, sign_sap_setup_url)
61
+        .send()
62
+        .await?
63
+        .error_for_status()?
64
+        .bytes()
65
+        .await?;
66
+
67
+    let sap_certificate_dict: Dictionary =
68
+        plist::from_bytes(&sap_certificate_response).map_err(|_| URLBagError::Parsing)?;
69
+
70
+    let sap_certificate = sap_certificate_dict
71
+        .get("sign-sap-setup-buffer")
72
+        .ok_or(URLBagError::Parsing)?
73
+        .as_data()
74
+        .ok_or(URLBagError::Parsing)?;
75
+
76
+    todo!()
77
+}
78
+
79
+#[derive(Debug, Error)]
46 80
 pub enum AuthError {
47
-    Apple(AppleError),
48
-    Anisette(ADIError),
49
-    Network(reqwest::Error),
81
+    #[error("Could not log-in to Apple servers: {0}")]
82
+    Apple(#[from] AppleError),
83
+    #[error("Cannot generate device authentication data: {0}")]
84
+    Anisette(#[from] ADIError),
85
+    #[error("Network error: {0}")]
86
+    Network(#[from] reqwest::Error),
87
+    #[error("Cannot find the endpoint in the URL bag.")]
50 88
     InvalidURLBag,
89
+    #[error("Cannot parse HTTP response")]
51 90
     Parsing,
52 91
 }
53 92
 
54
-impl Display for AuthError {
55
-    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
56
-        match self {
57
-            AuthError::Apple(err) => {
58
-                write!(f, "Could not log-in to Apple servers: {err}")
59
-            }
60
-            AuthError::Anisette(err) => {
61
-                write!(f, "Cannot generate device authentication data: {err}")
62
-            }
63
-            AuthError::Network(err) => {
64
-                write!(f, "Network error: {err}")
65
-            }
66
-            AuthError::InvalidURLBag => {
67
-                write!(f, "Cannot find the endpoint in the URL bag.")
68
-            }
69
-            AuthError::Parsing => {
70
-                write!(f, "Cannot parse HTTP response")
71
-            }
72
-        }
73
-    }
74
-}
75
-
76
-impl std::error::Error for AuthError {}
77
-
93
+///
94
+///
95
+/// # Arguments
96
+///
97
+/// * `http_session`: The HTTPSession to use for the requests.
98
+/// * `apple_id`: Apple Account identifier
99
+/// * `password`: Apple Account password (can be a PET)
100
+/// * `attempt`: Current attempt count. The first attempt should be numbered 1.
101
+///
102
+/// returns: Result<(), AuthError>
103
+///
104
+/// # Examples
105
+///
106
+/// ```
107
+///
108
+/// ```
78 109
 pub async fn login(
79 110
     http_session: &AnisetteHTTPSession<'_, '_>,
80 111
     apple_id: &str,
... ...
@@ -85,53 +116,60 @@ pub async fn login(
85 116
         .url_bag()
86 117
         .get("authenticateAccount")
87 118
         .and_then(Value::as_string)
88
-        .ok_or(AuthError::InvalidURLBag)?
89
-        .replace("sandbox", "p51-buy");
119
+        .ok_or(AuthError::InvalidURLBag)?;
90 120
 
91 121
     let adi_proxy = http_session.adi_proxy();
92 122
 
93
-    /*
94
-       "guid": "",
95
-       "kbsync": vec![],
96
-    */
123
+    let accounts = adi_proxy
124
+        .get_all_provisioned_accounts()?
125
+        .iter()
126
+        .map(|account| {
127
+            Value::Dictionary(dict! {
128
+                "dsid": account.ds_id,
129
+                "mid": BASE64_STANDARD.encode(&account.mid),
130
+                "otp": BASE64_STANDARD.encode(&account.otp),
131
+            })
132
+        })
133
+        .collect::<Vec<_>>();
97 134
 
98 135
     let request_plist = dict! {
99 136
         "appleId": apple_id,
100 137
         "attempt": attempt,
138
+        "auth-mid-otp": accounts,
101 139
         "createSession": true,
140
+        "guid": "A0DE61112331", // TODO
141
+        "machineName": "MacBook", // TODO
102 142
         "password": password,
103 143
         "passwordSettings": dict! {
104
-            "free": "always",
105
-            "paid": "always"
144
+            "free": "",
145
+            "paid": ""
106 146
         },
107
-        "rmp": "0",
147
+        // "kbsync": vec![],
148
+        "uuid": "3E72931A-9BFE-46EB-9EEA-3383CE091615", // TODO
108 149
         "why": "signIn",
109 150
     };
110 151
 
111 152
     let mut request_body = Vec::new();
112 153
     plist::to_writer_xml(&mut request_body, &request_plist).unwrap();
113 154
 
114
-    /*
155
+    // println!("request_body: {}", String::from_utf8(request_body).unwrap());
156
+
157
+    let signature = todo!();
158
+
115 159
     let response = http_session
116
-        .anisette_request_builder(GRANDSLAM_DSID, Method::POST, &authenticate_url)
117
-        .map_err(AuthError::Anisette)?
160
+        .anisette_request_builder(-2, Method::POST, authenticate_url)?
118 161
         .body(request_body)
162
+        .header("Content-Type", "application/x-apple-plist")
163
+        // .header("X-Apple-ActionSignature", signature)
119 164
         .send()
120 165
         .await
121
-        .and_then(|response| response.error_for_status())
122
-        .map_err(AuthError::Network)?
166
+        .and_then(|response| response.error_for_status())?
123 167
         .text()
124
-        .await
125
-        .map_err(AuthError::Network)?;
168
+        .await?;
126 169
 
127 170
     println!("response: {:?}", response);
128 171
 
129
-        .bytes()
130
-        .await
131
-        .map_err(AuthError::Network)?;
132
-
133
-
134
-
172
+    /*
135 173
     let response_plist: Dictionary =
136 174
         plist::from_bytes(&response).map_err(|err| AuthError::Parsing)?;
137 175
 
... ...
@@ -141,7 +179,7 @@ pub async fn login(
141 179
         .ok_or(AuthError::Parsing)?;
142 180
 
143 181
     println!("{:#?}", response_dict);
144
-    // */
145 182
 
146 183
     Ok(())
184
+    // */
147 185
 }
... ...
@@ -3,3 +3,4 @@ pub mod device;
3 3
 pub mod grandslam;
4 4
 pub mod http_session;
5 5
 pub mod itunes;
6
+pub mod plist_request;
... ...
@@ -0,0 +1,15 @@
1
+use plist::{Dictionary, Value};
2
+use reqwest::Body;
3
+
4
+pub fn plist_to_body(val: Value) -> Body {
5
+    // UNWRAP safety: I don't know any reason why the serializer should fail.
6
+    // If it happens, I would guess it's a serious flaw in the plist crate.
7
+    let mut request_body = Vec::new();
8
+    plist::to_writer_xml(&mut request_body, &val).expect("Failed to serialize plist?");
9
+
10
+    Body::from(request_body)
11
+}
12
+
13
+pub fn dict_to_body(dict: Dictionary) -> Body {
14
+    plist_to_body(Value::Dictionary(dict))
15
+}
... ...
@@ -8,7 +8,7 @@ plist.workspace = true
8 8
 
9 9
 tokio.workspace = true
10 10
 lief = "0.17"
11
-sha1 = "0.11.0-rc.3"
12
-sha2 = "0.11.0-rc.3"
11
+sha1.workspace = true
12
+sha2.workspace = true
13 13
 futures = "0.3.31"
14 14
 log = "0.4.28"
... ...
@@ -168,7 +168,9 @@ impl<'data> Library<'data> {
168 168
             }
169 169
         };
170 170
 
171
-        elf.shdr_relocs.iter().for_each(|(_, reloc_section)| relocate(reloc_section));
171
+        elf.shdr_relocs
172
+            .iter()
173
+            .for_each(|(_, reloc_section)| relocate(reloc_section));
172 174
 
173 175
         let gnu_hash_section = elf
174 176
             .section_headers
... ...
@@ -8,10 +8,13 @@ adi.workspace = true
8 8
 android-coreadi.workspace = true
9 9
 apple-account.workspace = true
10 10
 elf-loader.workspace = true
11
+xcode.workspace = true
11 12
 
12 13
 tokio.workspace = true
13 14
 libc = "0.2.176"
15
+reqwest = "0.13.1"
14 16
 env_logger = "0.11.8"
17
+base64 = "0.22.1"
15 18
 
16 19
 [target.'cfg(target_os = "macos")'.dependencies]
17 20
 adid-proxy.workspace = true
... ...
@@ -1,6 +1,6 @@
1 1
 use adi::proxy::ADIProxy;
2 2
 use android_coreadi::AndroidCoreADIProxy;
3
-use apple_account::bundle_information::{APPLE_TV_BUNDLE_INFORMATION, XCODE_BUNDLE_INFORMATION};
3
+use apple_account::bundle_information::APPLE_TV_BUNDLE_INFORMATION;
4 4
 use apple_account::device::Device;
5 5
 use apple_account::grandslam::{AuthOutcome, AuthenticatedHTTPSession};
6 6
 use apple_account::http_session::AnisetteHTTPSession;
... ...
@@ -24,7 +24,7 @@ async fn grandslam_test(
24 24
     // println!("{:#02X?}", proxy.get_all_provisioned_accounts());
25 25
 
26 26
     let http_session = AnisetteHTTPSession::new(
27
-        grandslam::http_session(device, XCODE_BUNDLE_INFORMATION).await?,
27
+        grandslam::http_session(device, APPLE_TV_BUNDLE_INFORMATION).await?,
28 28
         proxy,
29 29
     );
30 30
 
... ...
@@ -32,21 +32,54 @@ async fn grandslam_test(
32 32
         grandslam::provision(&http_session).await?;
33 33
     }
34 34
 
35
+    // Here we should fetch_auth_mode first to know if we should log-in with a password.
35 36
     let auth_outcome = grandslam::login(&http_session, apple_id, password).await?;
36 37
 
37 38
     match &auth_outcome {
38 39
         AuthOutcome::Success(server_provided_data)
39
-        | AuthOutcome::SecondaryActionRequired(server_provided_data, _) => {
40
+        | AuthOutcome::SecondaryActionRequired(Some(server_provided_data), _) => {
40 41
             match grandslam::parse_tokens_from_server_provided_data(server_provided_data) {
41
-                Some((auth_token, _)) => {
42
-                    let http_session = AuthenticatedHTTPSession::new(
43
-                        http_session,
44
-                        auth_token
45
-                    );
46
-                    let xcode_token =
47
-                        http_session.get_app_token("com.apple.gs.xcode.auth")
42
+                Some((auth_token, tokens)) => {
43
+                    /*
44
+                    let Some((_, token)) = tokens
45
+                        .iter()
46
+                        .find(|(name, _)| name == "com.apple.gs.idms.pet")
47
+                    else {
48
+                        todo!()
49
+                    };
50
+
51
+                    itunes_test(proxy, apple_id, token.token.as_str()).await?;
52
+                    // */
53
+
54
+                    let url = http_session
55
+                        .url_bag()
56
+                        .get("fetchUserInfo")
57
+                        .unwrap()
58
+                        .as_string()
59
+                        .unwrap()
60
+                        .to_owned();
61
+
62
+                    let (_, hb_token) = tokens
63
+                        .iter()
64
+                        .find(|(name, _)| name == "com.apple.gs.idms.hb")
65
+                        .unwrap();
66
+
67
+                    let http_session =
68
+                        AuthenticatedHTTPSession::new(http_session, auth_token, hb_token.clone());
69
+
70
+                    let text = http_session
71
+                        .authenticated_request_builder(reqwest::Method::GET, url.as_str())?
72
+                        .send()
73
+                        .await?
74
+                        .text()
48 75
                         .await?;
49
-                    println!("token: {:?}", xcode_token);
76
+
77
+                    println!("{}", text);
78
+
79
+                    // let xcode_token = http_session
80
+                    //    .get_app_token("com.apple.gs.xcode.auth")
81
+                    //    .await?;
82
+                    // println!("token: {:?}", xcode_token);
50 83
                 }
51 84
                 None => {
52 85
                     let AuthOutcome::SecondaryActionRequired(_, _secondary_action) = auth_outcome
... ...
@@ -57,7 +90,8 @@ async fn grandslam_test(
57 90
                 }
58 91
             }
59 92
         }
60
-        AuthOutcome::AnisetteResyncRequired(_)
93
+        AuthOutcome::SecondaryActionRequired(_, _)
94
+        | AuthOutcome::AnisetteResyncRequired(_)
61 95
         | AuthOutcome::AnisetteReprovisionRequired
62 96
         | AuthOutcome::UrlSwitchingRequired(_) => todo!(),
63 97
     }
... ...
@@ -83,6 +117,8 @@ async fn itunes_test(
83 117
 
84 118
     let auth_outcome = itunes::login(&http_session, apple_id, password, 1).await?;
85 119
 
120
+    println!("{:?}", auth_outcome);
121
+
86 122
     Ok(())
87 123
 }
88 124
 
... ...
@@ -97,7 +133,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
97 133
         println!("{}", code);
98 134
     }
99 135
     let proxy = {
100
-        let core_adi_data = fs::read("nodistrib/lib/x86_64/libCoreADI.so")?;
136
+        let core_adi_data = fs::read("nodistrib/lib/arm64-v8a/libCoreADI.so")?;
101 137
         let proxy = AndroidCoreADIProxy::load_library(core_adi_data)?;
102 138
 
103 139
         proxy.set_android_id("0123456789012345")?;
... ...
@@ -0,0 +1,12 @@
1
+[package]
2
+name = "xcode"
3
+version = "0.1.0"
4
+edition = "2024"
5
+
6
+[dependencies]
7
+apple-account.workspace = true
8
+adi.workspace = true
9
+plist.workspace = true
10
+plist-macros.workspace = true
11
+
12
+reqwest.workspace = true
... ...
@@ -0,0 +1,12 @@
1
+use apple_account::bundle_information::BundleInformation;
2
+use apple_account::grandslam::AppTokenIdentifier;
3
+
4
+/// From Xcode 16.4
5
+pub const XCODE_BUNDLE_INFORMATION: BundleInformation = BundleInformation {
6
+    bundle_name: "Xcode",
7
+    bundle_identifier: "com.apple.dt.Xcode",
8
+    bundle_version: "23792",
9
+};
10
+
11
+pub const XCODE_TOKEN_IDENTIFIER: AppTokenIdentifier =
12
+    AppTokenIdentifier("com.apple.gs.xcode.auth");
0 13