Improve error handling and fix issues

This commit is contained in:
nab138
2025-08-08 22:16:09 -04:00
parent a17b2be2ac
commit 74f5af717c
16 changed files with 475 additions and 437 deletions

354
Cargo.lock generated
View File

@@ -180,7 +180,7 @@ version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff"
dependencies = [
"bindgen",
"bindgen 0.69.5",
"cc",
"cmake",
"dunce",
@@ -202,12 +202,6 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "base64"
version = "0.21.7"
@@ -235,7 +229,7 @@ dependencies = [
"bitflags 2.9.1",
"cexpr",
"clang-sys",
"itertools",
"itertools 0.12.1",
"lazy_static",
"lazycell",
"log",
@@ -243,12 +237,32 @@ dependencies = [
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"rustc-hash 1.1.0",
"shlex",
"syn 2.0.104",
"which",
]
[[package]]
name = "bindgen"
version = "0.72.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f72209734318d0b619a5e0f5129918b848c416e122a3c4ce054e03cb87b726f"
dependencies = [
"bitflags 2.9.1",
"cexpr",
"clang-sys",
"itertools 0.13.0",
"log",
"prettyplease",
"proc-macro2",
"quote",
"regex",
"rustc-hash 2.1.1",
"shlex",
"syn 2.0.104",
]
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -273,7 +287,7 @@ version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
"generic-array 0.14.7",
]
[[package]]
@@ -282,31 +296,32 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
dependencies = [
"generic-array",
"generic-array 0.14.7",
]
[[package]]
name = "botan"
version = "0.11.1"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d4c7647d67c53194fa0740404c6c508880aef2bfe99a9868dbb4b86f090377"
checksum = "f23e39f9dbdfec8b4b6ba8509c8202a573b5d52fe213349c385d86656c89d7b5"
dependencies = [
"botan-sys",
]
[[package]]
name = "botan-src"
version = "0.30701.2"
version = "0.30900.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c4e1c7910f3b4712aed10e4259eca77e79ed84c1b023098c8eac596b993fc44"
checksum = "9bc24a437ed9a438eaace54677eead87931fcdf5cb9fef85567be2f82d8c5d92"
[[package]]
name = "botan-sys"
version = "0.11.1"
version = "1.20250506.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04285fa0c094cc9961fe435b1b279183db9394844ad82ce483aa6196c0e6da38"
checksum = "9a68b2bca80766adc60e9d88e99d958bba278a99ed616bf92b9d266c89dd2a9e"
dependencies = [
"botan-src",
"cc",
]
[[package]]
@@ -315,12 +330,6 @@ version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.10.1"
@@ -347,9 +356,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.31"
version = "1.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2"
checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e"
dependencies = [
"jobserver",
"libc",
@@ -381,7 +390,6 @@ dependencies = [
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"wasm-bindgen",
"windows-link",
]
@@ -424,9 +432,9 @@ checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "const-oid"
version = "0.7.1"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "constant_time_eq"
@@ -474,7 +482,7 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"generic-array 0.14.7",
"typenum",
]
@@ -492,11 +500,25 @@ checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b"
[[package]]
name = "der"
version = "0.5.1"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c"
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
dependencies = [
"const-oid",
"der_derive",
"flagset",
"zeroize",
]
[[package]]
name = "der_derive"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
@@ -630,6 +652,12 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "flagset"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe"
[[package]]
name = "flate2"
version = "1.1.2"
@@ -776,6 +804,15 @@ dependencies = [
"version_check",
]
[[package]]
name = "generic-array"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8c8444bc9d71b935156cc0ccab7f622180808af7867b1daae6547d773591703"
dependencies = [
"typenum",
]
[[package]]
name = "getrandom"
version = "0.2.16"
@@ -822,7 +859,7 @@ dependencies = [
"futures-core",
"futures-sink",
"futures-util",
"http",
"http 0.2.12",
"indexmap",
"slab",
"tokio",
@@ -832,9 +869,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.15.4"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
[[package]]
name = "hex"
@@ -871,6 +908,17 @@ dependencies = [
"itoa",
]
[[package]]
name = "http"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "http-body"
version = "0.4.6"
@@ -878,7 +926,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
dependencies = [
"bytes",
"http",
"http 0.2.12",
"pin-project-lite",
]
@@ -905,7 +953,7 @@ dependencies = [
"futures-core",
"futures-util",
"h2",
"http",
"http 0.2.12",
"http-body",
"httparse",
"httpdate",
@@ -925,7 +973,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
dependencies = [
"futures-util",
"http",
"http 0.2.12",
"hyper",
"rustls 0.21.12",
"tokio",
@@ -974,25 +1022,24 @@ name = "icloud_auth"
version = "0.1.0"
dependencies = [
"aes",
"base64 0.13.1",
"base64 0.22.1",
"botan",
"cbc",
"chrono",
"hmac",
"num-bigint",
"omnisette",
"pbkdf2 0.11.0",
"pkcs7",
"plist",
"rand 0.8.5",
"rand 0.9.2",
"reqwest",
"rustls 0.20.9",
"rustls-pemfile",
"rustls 0.23.31",
"rustls-pemfile 2.2.0",
"serde",
"serde_json",
"sha2",
"srp",
"thiserror 1.0.69",
"thiserror",
"tokio",
]
@@ -1095,7 +1142,7 @@ dependencies = [
"plist",
"rustls 0.23.31",
"serde",
"thiserror 2.0.12",
"thiserror",
"tokio",
"tokio-rustls 0.26.2",
]
@@ -1138,7 +1185,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
dependencies = [
"block-padding",
"generic-array",
"generic-array 0.14.7",
]
[[package]]
@@ -1176,8 +1223,10 @@ dependencies = [
"plist",
"serde",
"sha1",
"thiserror",
"uuid",
"zip",
"zsign-rust",
]
[[package]]
@@ -1189,6 +1238,15 @@ dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.15"
@@ -1497,7 +1555,7 @@ dependencies = [
"android-loader",
"anyhow",
"async-trait",
"base64 0.21.7",
"base64 0.22.1",
"chrono",
"dlopen2",
"futures-util",
@@ -1507,13 +1565,13 @@ dependencies = [
"objc",
"objc-foundation",
"plist",
"rand 0.8.5",
"rand 0.9.2",
"remove-async-await",
"reqwest",
"serde",
"serde_json",
"sha2",
"thiserror 1.0.69",
"thiserror",
"tokio-tungstenite",
"uuid",
]
@@ -1564,9 +1622,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-src"
version = "300.5.1+3.5.1"
version = "300.5.2+3.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "735230c832b28c000e3bc117119e6466a663ec73506bc0a9907ea4187508e42a"
checksum = "d270b79e2926f5150189d475bc7e9d2c69f9c4697b185fa917d5a32b792d21b4"
dependencies = [
"cc",
]
@@ -1637,12 +1695,13 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkcs7"
version = "0.3.0"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f7364e6d0e236473de91e042395d71e0e64715f99a60620b014a4a4c7d1619b"
checksum = "d79178be066405e0602bf3035946edef6b11b3f9dde46dfe5f8bfd7dea4b77e7"
dependencies = [
"der",
"spki",
"x509-cert",
]
[[package]]
@@ -1875,7 +1934,7 @@ dependencies = [
"futures-core",
"futures-util",
"h2",
"http",
"http 0.2.12",
"http-body",
"hyper",
"hyper-rustls",
@@ -1889,7 +1948,7 @@ dependencies = [
"percent-encoding",
"pin-project-lite",
"rustls 0.21.12",
"rustls-pemfile",
"rustls-pemfile 1.0.4",
"serde",
"serde_json",
"serde_urlencoded",
@@ -1904,25 +1963,10 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"webpki-roots",
"webpki-roots 0.25.4",
"winreg",
]
[[package]]
name = "ring"
version = "0.16.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
dependencies = [
"cc",
"libc",
"once_cell",
"spin",
"untrusted 0.7.1",
"web-sys",
"winapi",
]
[[package]]
name = "ring"
version = "0.17.14"
@@ -1933,7 +1977,7 @@ dependencies = [
"cfg-if",
"getrandom 0.2.16",
"libc",
"untrusted 0.9.0",
"untrusted",
"windows-sys 0.52.0",
]
@@ -1949,6 +1993,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc-hash"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
[[package]]
name = "rustix"
version = "0.38.44"
@@ -1975,18 +2025,6 @@ dependencies = [
"windows-sys 0.60.2",
]
[[package]]
name = "rustls"
version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99"
dependencies = [
"log",
"ring 0.16.20",
"sct",
"webpki",
]
[[package]]
name = "rustls"
version = "0.21.12"
@@ -1994,7 +2032,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
dependencies = [
"log",
"ring 0.17.14",
"ring",
"rustls-webpki 0.101.7",
"sct",
]
@@ -2023,6 +2061,15 @@ dependencies = [
"base64 0.21.7",
]
[[package]]
name = "rustls-pemfile"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "rustls-pki-types"
version = "1.12.0"
@@ -2038,8 +2085,8 @@ version = "0.101.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
dependencies = [
"ring 0.17.14",
"untrusted 0.9.0",
"ring",
"untrusted",
]
[[package]]
@@ -2049,16 +2096,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc"
dependencies = [
"aws-lc-rs",
"ring 0.17.14",
"ring",
"rustls-pki-types",
"untrusted 0.9.0",
"untrusted",
]
[[package]]
name = "rustversion"
version = "1.0.21"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
@@ -2081,8 +2128,8 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
dependencies = [
"ring 0.17.14",
"untrusted 0.9.0",
"ring",
"untrusted",
]
[[package]]
@@ -2188,9 +2235,9 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "slab"
version = "0.4.10"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
[[package]]
name = "smallvec"
@@ -2218,18 +2265,13 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "spki"
version = "0.5.4"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27"
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
dependencies = [
"base64ct",
"der",
]
@@ -2237,9 +2279,9 @@ dependencies = [
name = "srp"
version = "0.6.0"
dependencies = [
"base64 0.21.7",
"base64 0.22.1",
"digest",
"generic-array",
"generic-array 1.2.0",
"lazy_static",
"num-bigint",
"subtle",
@@ -2335,33 +2377,13 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl 1.0.69",
]
[[package]]
name = "thiserror"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl 2.0.12",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
"thiserror-impl",
]
[[package]]
@@ -2465,17 +2487,18 @@ dependencies = [
[[package]]
name = "tokio-tungstenite"
version = "0.20.1"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
checksum = "489a59b6730eda1b0171fcfda8b121f4bee2b35cba8645ca35c5f7ba3eb736c1"
dependencies = [
"futures-util",
"log",
"rustls 0.21.12",
"rustls 0.23.31",
"rustls-pki-types",
"tokio",
"tokio-rustls 0.24.1",
"tokio-rustls 0.26.2",
"tungstenite",
"webpki-roots",
"webpki-roots 0.26.11",
]
[[package]]
@@ -2524,21 +2547,20 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "tungstenite"
version = "0.20.1"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9"
checksum = "eadc29d668c91fcc564941132e17b28a7ceb2f3ebf0b9dae3e03fd7a6748eb0d"
dependencies = [
"byteorder",
"bytes",
"data-encoding",
"http",
"http 1.3.1",
"httparse",
"log",
"rand 0.8.5",
"rustls 0.21.12",
"rand 0.9.2",
"rustls 0.23.31",
"rustls-pki-types",
"sha1",
"thiserror 1.0.69",
"url",
"thiserror",
"utf-8",
]
@@ -2554,12 +2576,6 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "untrusted"
version = "0.9.0"
@@ -2736,22 +2752,30 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "webpki"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53"
dependencies = [
"ring 0.17.14",
"untrusted 0.9.0",
]
[[package]]
name = "webpki-roots"
version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
[[package]]
name = "webpki-roots"
version = "0.26.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
dependencies = [
"webpki-roots 1.0.2",
]
[[package]]
name = "webpki-roots"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "which"
version = "4.4.2"
@@ -3092,6 +3116,17 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
[[package]]
name = "x509-cert"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94"
dependencies = [
"const-oid",
"der",
"spki",
]
[[package]]
name = "xmas-elf"
version = "0.9.1"
@@ -3270,6 +3305,17 @@ dependencies = [
"simd-adler32",
]
[[package]]
name = "zsign-rust"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9190123f81d18e157f6f3aab47f19b786538b158417f06ee9348c63163b23fca"
dependencies = [
"bindgen 0.72.0",
"cc",
"openssl-sys",
]
[[package]]
name = "zstd"
version = "0.13.3"

View File

@@ -9,13 +9,15 @@ vendored-openssl = ["openssl/vendored"]
vendored-botan = ["icloud_auth/vendored-botan"]
[dependencies]
serde = { version = "1.0.219", features = ["derive"] }
serde = { version = "1", features = ["derive"] }
plist = { version = "1.7.2" }
icloud_auth = {path = "./apple-private-apis/icloud-auth" }
uuid = "1.17.0"
zip = "4.3.0"
hex = "0.4.3"
sha1 = "0.10.6"
sha1 = "0.10"
idevice = { version = "0.1.37", features = ["afc", "usbmuxd", "installation_proxy"] }
openssl = "0.10.73"
futures = "0.3.31"
zsign-rust = "0.1"
thiserror = "2.0.12"

View File

@@ -8,27 +8,26 @@ default = []
vendored-botan = ["botan/vendored"]
[dependencies]
serde = { version = "1.0.219", features = ["derive"] }
serde_json = { version = "1.0.142" }
base64 = "0.13.1"
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1" }
base64 = "0.22"
srp = { version = "0.6.0", path = "./rustcrypto-srp" }
pbkdf2 = { version = "0.11.0" }
sha2 = { version = "0.10.6" }
rand = { version = "0.8.5" }
rustls = { version = "0.20.7" }
rustls-pemfile = { version = "1.0.1" }
plist = { version = "1.7.2" }
pbkdf2 = "0.11"
sha2 = "0.10"
rand = "0.9"
rustls = "0.23"
rustls-pemfile = "2.2"
plist = "1.7.2"
hmac = "0.12.1"
num-bigint = "0.4.3"
cbc = { version = "0.1.2", features = ["std"] }
aes = "0.8.2"
pkcs7 = "0.3.0"
pkcs7 = "0.4.1"
reqwest = { version = "0.11.14", features = ["blocking", "json", "default-tls"] }
omnisette = {path = "../omnisette", features = ["remote-anisette-v3"]}
thiserror = "1.0.58"
thiserror = "2"
tokio = "1"
botan = { version = "0.11.1" }
chrono = { version = "0.4", features = ["serde"] }
botan = "0.12.0"
[dev-dependencies]
tokio = { version = "1", features = ["rt", "macros"] }

View File

@@ -14,15 +14,15 @@ rust-version = "1.56"
[dependencies]
num-bigint = "0.4"
generic-array = "0.14"
generic-array = "1"
digest = "0.10"
lazy_static = "1.2"
subtle = "2.4"
base64 = "0.21.0"
base64 = "0.22"
[dev-dependencies]
hex-literal = "0.3"
hex-literal = "1"
num-traits = "0.2"
rand = "0.8"
sha1 = "0.10.6"
sha2 = "0.10.8"
rand = "0.9"
sha1 = "0.10"
sha2 = "0.10"

View File

@@ -15,11 +15,7 @@ impl AnisetteData {
let mut b = AnisetteHeaders::get_anisette_headers_provider(config.clone())?;
let base_headers = b.provider.get_authentication_headers().await?;
Ok(AnisetteData {
base_headers,
generated_at: SystemTime::now(),
config,
})
Ok(AnisetteData { base_headers, generated_at: SystemTime::now(), config })
}
pub fn needs_refresh(&self) -> bool {
@@ -69,7 +65,7 @@ impl AnisetteData {
"X-Apple-App-Info".to_owned(),
"com.apple.gs.xcode.auth".to_owned(),
);
headers.insert("X-Xcode-Version".to_owned(), "14.2 (14C18)".to_owned());
headers.insert("X-Xcode-Version".to_owned(), "11.2 (11B41)".to_owned());
}
if cpd {

View File

@@ -1,5 +1,6 @@
use crate::{anisette::AnisetteData, Error};
use aes::cipher::block_padding::Pkcs7;
use base64::{engine::general_purpose, Engine};
use botan::Cipher;
use cbc::cipher::{BlockDecryptMut, KeyIvInit};
use hmac::{Hmac, Mac};
@@ -269,7 +270,6 @@ impl AppleAccount {
return Err(err_check.err().unwrap());
}
// --- D code logic starts here ---
let encrypted_token = res
.get("et")
.ok_or(Error::Parse)?
@@ -286,7 +286,7 @@ impl AppleAccount {
"Encrypted token is in an unknown format.".to_string(),
));
}
let iv = &encrypted_token[3..19]; // 16 bytes
let iv = &encrypted_token[3..19];
let ciphertext_and_tag = &encrypted_token[19..];
if sk.len() != 32 {
@@ -296,8 +296,6 @@ impl AppleAccount {
return Err(Error::Parse);
}
// Botan AES-256/GCM decryption with 16-byte IV and 3-byte AAD
// true = encrypt, false = decrypt
let mut cipher = Cipher::new("AES-256/GCM", botan::CipherDirection::Decrypt)
.map_err(|_| Error::Parse)?;
cipher.set_key(sk).map_err(|_| Error::Parse)?;
@@ -357,14 +355,17 @@ impl AppleAccount {
///
/// let anisette = AnisetteData::new();
/// let account = AppleAccount::login(
/// || ("test@waffle.me", "password")
/// || "123123",
/// || Ok(("test@waffle.me", "password"))
/// || Ok("123123"),
/// anisette
/// );
/// ```
/// Note: You would not provide the 2FA code like this, you would have to actually ask input for it.
//TODO: add login_with_anisette and login, where login autodetcts anisette
pub async fn login_with_anisette<F: Fn() -> Result<(String, String), String>, G: Fn() -> Result<String, String>>(
pub async fn login_with_anisette<
F: Fn() -> Result<(String, String), String>,
G: Fn() -> Result<String, String>,
>(
appleid_closure: F,
tfa_closure: G,
anisette: AnisetteData,
@@ -378,15 +379,25 @@ impl AppleAccount {
match response {
LoginState::NeedsDevice2FA => response = _self.send_2fa_to_devices().await?,
LoginState::Needs2FAVerification => {
response = _self.verify_2fa(tfa_closure().map_err(|e| {
Error::AuthSrpWithMessage(0, format!("Failed to get 2FA code: {}", e))
})?).await?
response = _self
.verify_2fa(tfa_closure().map_err(|e| {
Error::AuthSrpWithMessage(0, format!("Failed to get 2FA code: {}", e))
})?)
.await?
}
LoginState::NeedsSMS2FA => response = _self.send_sms_2fa_to_devices(1).await?,
LoginState::NeedsSMS2FAVerification(body) => {
response = _self.verify_sms_2fa(tfa_closure().map_err(|e| {
Error::AuthSrpWithMessage(0, format!("Failed to get SMS 2FA code: {}", e))
})?, body).await?
response = _self
.verify_sms_2fa(
tfa_closure().map_err(|e| {
Error::AuthSrpWithMessage(
0,
format!("Failed to get SMS 2FA code: {}", e),
)
})?,
body,
)
.await?
}
LoginState::NeedsLogin => {
response = _self.login_email_pass(&username, &password).await?
@@ -735,7 +746,7 @@ impl AppleAccount {
let dsid = spd.get("adsid").unwrap().as_string().unwrap();
let token = spd.get("GsIdmsToken").unwrap().as_string().unwrap();
let identity_token = base64::encode(format!("{}:{}", dsid, token));
let identity_token = general_purpose::STANDARD.encode(format!("{}:{}", dsid, token));
let valid_anisette = self.get_anisette().await;

View File

@@ -1,8 +1,5 @@
// use icloud_auth::ani
use std::sync::Arc;
use base64::engine::{general_purpose, Engine};
use num_bigint::BigUint;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use srp::{
client::{SrpClient, SrpClientVerifier},
@@ -11,15 +8,18 @@ use srp::{
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn auth_debug() {
// not a real account
let bytes_a = base64::decode("XChHXELsQ+ljxTFbvRMUsGJxiDIlOh9f8e+JzoegmVcOdAXXtPNzkHpAbAgSjyA+vXrTA93+BUu8EJ9+4xZu9g==").unwrap();
let bytes_a = general_purpose::STANDARD.decode("XChHXELsQ+ljxTFbvRMUsGJxiDIlOh9f8e+JzoegmVcOdAXXtPNzkHpAbAgSjyA+vXrTA93+BUu8EJ9+4xZu9g==").unwrap();
let username = "apple3@f1sh.me";
let password = "WaffleTest123";
let salt = base64::decode("6fK6ailLUcp2kJswJVrKjQ==").unwrap();
let salt = general_purpose::STANDARD
.decode("6fK6ailLUcp2kJswJVrKjQ==")
.unwrap();
let iters = 20832;
let mut password_hasher = sha2::Sha256::new();
@@ -41,7 +41,9 @@ mod tests {
// apub: N2XHuh/4P1urPoBvDocF0RCRIl2pliZYqg9p6wGH0nnJdckJPn3M00jEqoM4teqH03HjG1murdcZiNHb5YayufW//+asW01XB7nYIIVvGiUFLRypYITEKYWBQ6h2q02GaZspYJKy98V8Fwcvr0ri+al7zJo1X1aoRKINyjV5TywhhwmTleI1qJkf+JBRYKKqO1XFtOTpQsysWD3ZJdK3K78kSgT3q0kXE3oDRMiHPAO77GFJZErYTuvI6QPRbOgcrn+RKV6AsjR5tUQAoSGRdtibdZTAQijJg788qVg+OFVCNZoY9GYVxa+Ze1bPGdkkgCYicTE8iNFG9KlJ+QpKgQ==
let a_random = base64::decode("ywN1O32vmBogb5Fyt9M7Tn8bbzLtDDbcYgPFpSy8n9E=").unwrap();
let a_random = general_purpose::STANDARD
.decode("ywN1O32vmBogb5Fyt9M7Tn8bbzLtDDbcYgPFpSy8n9E=")
.unwrap();
let client = SrpClient::<Sha256>::new(&G_2048);
let a_pub_compute =
@@ -49,14 +51,21 @@ mod tests {
// expect it to be same to a_pub
println!(
"compute a_pub: {:?}",
base64::encode(&a_pub_compute.to_bytes_be())
general_purpose::STANDARD.encode(&a_pub_compute.to_bytes_be())
);
let b_pub = base64::decode("HlWxsRmNi/9DCGxYCoqCTfdSvpbx3mrgFLQfOsgf3Rojn7MQQN/g63PwlBghUcVVB4//yAaRRnz/VIByl8thA9AKuVZl8k52PAHKSh4e7TuXSeYCFr0+GYu8/hFdMDl42219uzSuOXuaKGVKq6hxEAf3n3uXXgQRkXWtLFJ5nn1wq/emf46hYAHzc/pYyvckAdh9WDCw95IXbzKD8LcPw/0ZQoydMuXgW2ZKZ52fiyEs94IZ7L5RLL7jY1nVdwtsp2fxeqiZ3DNmVZ2GdNrbJGT//160tyd2evtUtehr8ygXNzjWdjV0cc4+1F38ywSPFyieVzVTYzDywRllgo3A5A==").unwrap();
println!("fixed b_pub: {:?}", base64::encode(&b_pub));
let b_pub = general_purpose::STANDARD.decode("HlWxsRmNi/9DCGxYCoqCTfdSvpbx3mrgFLQfOsgf3Rojn7MQQN/g63PwlBghUcVVB4//yAaRRnz/VIByl8thA9AKuVZl8k52PAHKSh4e7TuXSeYCFr0+GYu8/hFdMDl42219uzSuOXuaKGVKq6hxEAf3n3uXXgQRkXWtLFJ5nn1wq/emf46hYAHzc/pYyvckAdh9WDCw95IXbzKD8LcPw/0ZQoydMuXgW2ZKZ52fiyEs94IZ7L5RLL7jY1nVdwtsp2fxeqiZ3DNmVZ2GdNrbJGT//160tyd2evtUtehr8ygXNzjWdjV0cc4+1F38ywSPFyieVzVTYzDywRllgo3A5A==").unwrap();
println!(
"fixed b_pub: {:?}",
general_purpose::STANDARD.encode(&b_pub)
);
println!("");
println!("salt: {:?} iterations: {:?}", base64::encode(&salt), iters);
println!(
"salt: {:?} iterations: {:?}",
general_purpose::STANDARD.encode(&salt),
iters
);
let verifier: SrpClientVerifier<Sha256> = SrpClient::<Sha256>::process_reply(
&client,

View File

@@ -10,12 +10,12 @@ default = ["remote-anisette", "dep:remove-async-await"]
remote-anisette-v3 = ["async", "dep:serde", "dep:serde_json", "dep:tokio-tungstenite", "dep:futures-util", "dep:chrono"]
[dependencies]
base64 = "0.21"
hex = "0.4.3"
base64 = "0.22"
hex = "0.4"
plist = "1.4"
reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "rustls-tls", "gzip"] }
rand = "0.8"
sha2 = "0.10.8"
rand = "0.9"
sha2 = "0.10"
uuid = { version = "1.3", features = [ "v4", "fast-rng", "macro-diagnostics" ] }
android-loader = { git = "https://github.com/Dadoum/android-loader", branch = "bigger_pages" }
libc = "0.2"
@@ -23,11 +23,11 @@ log = "0.4"
async-trait = { version = "0.1", optional = true }
remove-async-await = { version = "1.0", optional = true }
serde = { version = "1.0", features = ["derive"], optional = true }
serde_json = { version = "1.0.142", optional = true }
tokio-tungstenite = { version = "0.20.1", optional = true, features = ["rustls-tls-webpki-roots"] }
serde_json = { version = "1.0.115", optional = true }
tokio-tungstenite = { version = "0.27.0", optional = true, features = ["rustls-tls-webpki-roots"] }
futures-util = { version = "0.3.28", optional = true }
chrono = { version = "0.4.37", optional = true }
thiserror = "1.0.58"
thiserror = "2"
anyhow = "1.0.81"
[target.'cfg(target_os = "macos")'.dependencies]

View File

@@ -46,7 +46,7 @@ pub enum ADIError {
ReqwestError(#[from] reqwest::Error),
Base64Error(#[from] base64::DecodeError),
InvalidHeaderValue(#[from] InvalidHeaderValue),
IOError(#[from] io::Error)
IOError(#[from] io::Error),
}
impl ADIError {
@@ -95,7 +95,7 @@ pub struct RequestOTPData {
}
#[cfg_attr(feature = "async", async_trait::async_trait(?Send))]
pub trait ADIProxy: Send + Sync {
pub trait ADIProxy: Send + Sync {
fn erase_provisioning(&mut self, ds_id: i64) -> Result<(), ADIError>;
fn synchronize(&mut self, ds_id: i64, sim: &[u8]) -> Result<SynchronizeData, ADIError>;
fn destroy_provisioning_session(&mut self, session: u32) -> Result<(), ADIError>;
@@ -291,7 +291,9 @@ pub struct ADIProxyAnisetteProvider<ProxyType: ADIProxy + 'static> {
impl<ProxyType: ADIProxy + 'static> ADIProxyAnisetteProvider<ProxyType> {
/// If you use this method, you are expected to set the identifier yourself.
pub fn without_identifier(adi_proxy: ProxyType) -> Result<ADIProxyAnisetteProvider<ProxyType>, ADIError> {
pub fn without_identifier(
adi_proxy: ProxyType,
) -> Result<ADIProxyAnisetteProvider<ProxyType>, ADIError> {
Ok(ADIProxyAnisetteProvider { adi_proxy })
}
@@ -309,7 +311,7 @@ impl<ProxyType: ADIProxy + 'static> ADIProxyAnisetteProvider<ProxyType> {
if identifier_file.metadata()?.len() == IDENTIFIER_LENGTH as u64 {
identifier_file.read_exact(&mut identifier)?;
} else {
rand::thread_rng().fill_bytes(&mut identifier);
rand::rng().fill_bytes(&mut identifier);
identifier_file.write_all(&identifier)?;
}

View File

@@ -6,9 +6,9 @@
use crate::adi_proxy::{ADIProxyAnisetteProvider, ConfigurableADIProxy};
use crate::anisette_headers_provider::AnisetteHeadersProvider;
use adi_proxy::ADIError;
use std::io;
use std::path::PathBuf;
use adi_proxy::ADIError;
use thiserror::Error;
pub mod adi_proxy;
@@ -58,7 +58,7 @@ pub enum AnisetteError {
#[error("Missing Libraries")]
MissingLibraries,
#[error("{0}")]
Anyhow(#[from] anyhow::Error),
Anyhow(#[from] anyhow::Error)
}
pub const DEFAULT_ANISETTE_URL: &str = "https://ani.f1sh.me/";
@@ -85,7 +85,7 @@ impl AnisetteConfiguration {
anisette_url: DEFAULT_ANISETTE_URL.to_string(),
anisette_url_v3: DEFAULT_ANISETTE_URL_V3.to_string(),
configuration_path: PathBuf::new(),
macos_serial: "0".to_string(),
macos_serial: "0".to_string()
}
}
@@ -157,15 +157,10 @@ impl AnisetteHeaders {
#[cfg(feature = "remote-anisette-v3")]
return Ok(AnisetteHeadersProviderRes::remote(Box::new(
remote_anisette_v3::RemoteAnisetteProviderV3::new(
configuration.anisette_url_v3,
configuration.configuration_path.clone(),
configuration.macos_serial.clone(),
),
remote_anisette_v3::RemoteAnisetteProviderV3::new(configuration.anisette_url_v3, configuration.configuration_path.clone(), configuration.macos_serial.clone()),
)));
#[cfg(feature = "remote-anisette")]
#[allow(unreachable_code)]
return Ok(AnisetteHeadersProviderRes::remote(Box::new(
remote_anisette::RemoteAnisetteProvider::new(configuration.anisette_url),
)));

View File

@@ -99,7 +99,7 @@ pub struct AnisetteState {
impl Default for AnisetteState {
fn default() -> Self {
AnisetteState {
keychain_identifier: rand::thread_rng().gen::<[u8; 16]>(),
keychain_identifier: rand::rng().random::<[u8; 16]>(),
adi_pb: None,
}
}
@@ -211,7 +211,7 @@ impl AnisetteClient {
.header("X-Apple-I-MD-LU", encode_hex(&state.md_lu()))
.header("X-Mme-Device-Id", state.device_id())
.header("X-Apple-I-Client-Time", dt.format("%+").to_string())
.header("X-Apple-I-TimeZone", "EDT")
.header("X-Apple-I-TimeZone", "UTC")
.header("X-Apple-Locale", "en_US")
}
@@ -352,7 +352,7 @@ impl AnisetteClient {
identifier: base64_encode(&state.keychain_identifier),
};
connection
.send(Message::Text(serde_json::to_string(&identifier)?))
.send(Message::Text(serde_json::to_string(&identifier)?.into()))
.await?;
}
ProvisionInput::GiveStartProvisioningData => {
@@ -390,7 +390,7 @@ impl AnisetteClient {
spim: spim.to_string(),
};
connection
.send(Message::Text(serde_json::to_string(&spim)?))
.send(Message::Text(serde_json::to_string(&spim)?.into()))
.await?;
}
ProvisionInput::GiveEndProvisioningData { cpim } => {
@@ -427,7 +427,9 @@ impl AnisetteClient {
tk: response.get("tk").unwrap().as_string().unwrap(),
};
connection
.send(Message::Text(serde_json::to_string(&end_provisioning)?))
.send(Message::Text(
serde_json::to_string(&end_provisioning)?.into(),
))
.await?;
}
ProvisionInput::ProvisioningSuccess { adi_pb } => {

View File

@@ -66,11 +66,16 @@ pub struct StoreServicesCoreADIProxy<'lt> {
}
impl StoreServicesCoreADIProxy<'_> {
pub fn new<'lt>(library_path: &PathBuf) -> Result<StoreServicesCoreADIProxy<'lt>, AnisetteError> {
pub fn new<'lt>(
library_path: &PathBuf,
) -> Result<StoreServicesCoreADIProxy<'lt>, AnisetteError> {
Self::with_custom_provisioning_path(library_path, library_path)
}
pub fn with_custom_provisioning_path<'lt>(library_path: &PathBuf, provisioning_path: &PathBuf) -> Result<StoreServicesCoreADIProxy<'lt>, AnisetteError> {
pub fn with_custom_provisioning_path<'lt>(
library_path: &PathBuf,
provisioning_path: &PathBuf,
) -> Result<StoreServicesCoreADIProxy<'lt>, AnisetteError> {
// Should be safe if the library is correct.
unsafe {
LoaderHelpers::setup_hooks();
@@ -104,12 +109,8 @@ impl StoreServicesCoreADIProxy<'_> {
.ok_or(AnisetteError::InvalidLibraryFormat)?,
);
let path = CString::new(
native_library_path
.to_str()
.ok_or(AnisetteError::Misc)?,
)
.unwrap();
let path =
CString::new(native_library_path.to_str().ok_or(AnisetteError::Misc)?).unwrap();
assert_eq!((adi_load_library_with_path)(path.as_ptr() as *const u8), 0);
let adi_set_android_id = store_services_core
@@ -163,9 +164,7 @@ impl StoreServicesCoreADIProxy<'_> {
adi_otp_request: std::mem::transmute(adi_otp_request),
};
proxy.set_provisioning_path(
provisioning_path.to_str().ok_or(AnisetteError::Misc)?,
)?;
proxy.set_provisioning_path(provisioning_path.to_str().ok_or(AnisetteError::Misc)?)?;
Ok(proxy)
}
@@ -370,7 +369,7 @@ unsafe fn __errno_location() -> *mut i32 {
#[sysv64]
fn arc4random() -> u32 {
rand::thread_rng().gen()
rand::rng().random()
}
#[sysv64]
@@ -412,10 +411,10 @@ impl LoaderHelpers {
#[cfg(test)]
mod tests {
use crate::AnisetteError;
use crate::{AnisetteConfiguration, AnisetteHeaders};
use log::info;
use std::path::PathBuf;
use crate::AnisetteError;
#[cfg(not(feature = "async"))]
#[test]
@@ -436,7 +435,6 @@ mod tests {
#[cfg(feature = "async")]
#[tokio::test]
async fn fetch_anisette_ssc_async() -> Result<(), AnisetteError> {
crate::tests::init_logger();
let mut provider = AnisetteHeaders::get_ssc_anisette_headers_provider(

View File

@@ -32,7 +32,7 @@ impl CertificateIdentity {
let hash_string = hex::encode(hasher.finalize()).to_lowercase();
let key_path = configuration_path.join("keys").join(hash_string);
fs::create_dir_all(&key_path)
.map_err(|e| Error::Certificate(format!("Failed to create key directory: {}", e)))?;
.map_err(|e| Error::Filesystem(format!("Failed to create key directory: {}", e)))?;
let key_file = key_path.join("key.pem");
let cert_file = key_path.join("cert.pem");
@@ -54,7 +54,7 @@ impl CertificateIdentity {
.private_key_to_pem_pkcs8()
.map_err(|e| Error::Certificate(format!("Failed to encode private key: {}", e)))?;
fs::write(&key_file, pem_data)
.map_err(|e| Error::Certificate(format!("Failed to save key file: {}", e)))?;
.map_err(|e| Error::Filesystem(format!("Failed to save key file: {}", e)))?;
key
};
@@ -75,7 +75,7 @@ impl CertificateIdentity {
Error::Certificate(format!("Failed to encode certificate to PEM: {}", e))
})?;
fs::write(&cert_identity.cert_file, cert_pem).map_err(|e| {
Error::Certificate(format!("Failed to save certificate file: {}", e))
Error::Filesystem(format!("Failed to save certificate file: {}", e))
})?;
return Ok(cert_identity);
@@ -200,7 +200,7 @@ impl CertificateIdentity {
Error::Certificate(format!("Failed to encode certificate to PEM: {}", e))
})?;
fs::write(&self.cert_file, cert_pem)
.map_err(|e| Error::Certificate(format!("Failed to save certificate file: {}", e)))?;
.map_err(|e| Error::Filesystem(format!("Failed to save certificate file: {}", e)))?;
self.certificate = Some(certificate);

View File

@@ -56,7 +56,7 @@ impl DeveloperSession {
if let ICloudError::AuthSrpWithMessage(code, message) = e {
Error::DeveloperSession(code, format!("Developer request failed: {}", message))
} else {
Error::Generic
Error::Generic("Failed to send developer request".to_string())
}
})?;
@@ -85,20 +85,22 @@ impl DeveloperSession {
let teams = response
.get("teams")
.and_then(|v| v.as_array())
.ok_or(Error::Parse)?;
.ok_or(Error::Parse("teams".to_string()))?;
let mut result = Vec::new();
for team in teams {
let dict = team.as_dictionary().ok_or(Error::Parse)?;
let dict = team
.as_dictionary()
.ok_or(Error::Parse("team".to_string()))?;
let name = dict
.get("name")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("name".to_string()))?
.to_string();
let team_id = dict
.get("teamId")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("teamId".to_string()))?
.to_string();
result.push(DeveloperTeam {
_name: name,
@@ -140,25 +142,27 @@ impl DeveloperSession {
let devices = response
.get("devices")
.and_then(|v| v.as_array())
.ok_or(Error::Parse)?;
.ok_or(Error::Parse("devices".to_string()))?;
let mut result = Vec::new();
for device in devices {
let dict = device.as_dictionary().ok_or(Error::Parse)?;
let dict = device
.as_dictionary()
.ok_or(Error::Parse("device".to_string()))?;
let device_id = dict
.get("deviceId")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("deviceId".to_string()))?
.to_string();
let name = dict
.get("name")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("name".to_string()))?
.to_string();
let device_number = dict
.get("deviceNumber")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("deviceNumber".to_string()))?
.to_string();
result.push(DeveloperDevice {
_device_id: device_id,
@@ -187,22 +191,22 @@ impl DeveloperSession {
let device_dict = response
.get("device")
.and_then(|v| v.as_dictionary())
.ok_or(Error::Parse)?;
.ok_or(Error::Parse("device".to_string()))?;
let device_id = device_dict
.get("deviceId")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("deviceId".to_string()))?
.to_string();
let name = device_dict
.get("name")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("name".to_string()))?
.to_string();
let device_number = device_dict
.get("deviceNumber")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("deviceNumber".to_string()))?
.to_string();
Ok(DeveloperDevice {
@@ -226,25 +230,27 @@ impl DeveloperSession {
let certs = response
.get("certificates")
.and_then(|v| v.as_array())
.ok_or(Error::Parse)?;
.ok_or(Error::Parse("certificates".to_string()))?;
let mut result = Vec::new();
for cert in certs {
let dict = cert.as_dictionary().ok_or(Error::Parse)?;
let dict = cert
.as_dictionary()
.ok_or(Error::Parse("certificate".to_string()))?;
let name = dict
.get("name")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("name".to_string()))?
.to_string();
let certificate_id = dict
.get("certificateId")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("certificateId".to_string()))?
.to_string();
let serial_number = dict
.get("serialNumber")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("serialNumber".to_string()))?
.to_string();
let machine_name = dict
.get("machineName")
@@ -254,7 +260,7 @@ impl DeveloperSession {
let cert_content = dict
.get("certContent")
.and_then(|v| v.as_data())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("certContent".to_string()))?
.to_vec();
result.push(DevelopmentCertificate {
@@ -309,11 +315,11 @@ impl DeveloperSession {
let cert_dict = response
.get("certRequest")
.and_then(|v| v.as_dictionary())
.ok_or(Error::Parse)?;
.ok_or(Error::Parse("certRequest".to_string()))?;
let id = cert_dict
.get("certRequestId")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("certRequestId".to_string()))?
.to_string();
Ok(id)
@@ -333,35 +339,37 @@ impl DeveloperSession {
let app_ids = response
.get("appIds")
.and_then(|v| v.as_array())
.ok_or(Error::Parse)?;
.ok_or(Error::Parse("appIds".to_string()))?;
let mut result = Vec::new();
for app_id in app_ids {
let dict = app_id.as_dictionary().ok_or(Error::Parse)?;
let dict = app_id
.as_dictionary()
.ok_or(Error::Parse("appId".to_string()))?;
let name = dict
.get("name")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("name".to_string()))?
.to_string();
let app_id_id = dict
.get("appIdId")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("appIdId".to_string()))?
.to_string();
let identifier = dict
.get("identifier")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("identifier".to_string()))?
.to_string();
let features = dict
.get("features")
.and_then(|v| v.as_dictionary())
.ok_or(Error::Parse)?;
.ok_or(Error::Parse("features".to_string()))?;
let expiration_date = if dict.contains_key("expirationDate") {
Some(
dict.get("expirationDate")
.and_then(|v| v.as_date())
.ok_or(Error::Parse)?,
.ok_or(Error::Parse("expirationDate".to_string()))?,
)
} else {
None
@@ -379,11 +387,11 @@ impl DeveloperSession {
let max_quantity = response
.get("maxQuantity")
.and_then(|v| v.as_unsigned_integer())
.ok_or(Error::Parse)?;
.ok_or(Error::Parse("maxQuantity".to_string()))?;
let available_quantity = response
.get("availableQuantity")
.and_then(|v| v.as_unsigned_integer())
.ok_or(Error::Parse)?;
.ok_or(Error::Parse("availableQuantity".to_string()))?;
Ok(ListAppIdsResponse {
app_ids: result,
@@ -436,11 +444,11 @@ impl DeveloperSession {
let cert_dict = response
.get("appId")
.and_then(|v| v.as_dictionary())
.ok_or(Error::Parse)?;
.ok_or(Error::Parse("appId".to_string()))?;
let feats = cert_dict
.get("features")
.and_then(|v| v.as_dictionary())
.ok_or(Error::Parse)?;
.ok_or(Error::Parse("features".to_string()))?;
Ok(feats.clone())
}
@@ -475,25 +483,27 @@ impl DeveloperSession {
let app_groups = response
.get("applicationGroupList")
.and_then(|v| v.as_array())
.ok_or(Error::Parse)?;
.ok_or(Error::Parse("applicationGroupList".to_string()))?;
let mut result = Vec::new();
for app_group in app_groups {
let dict = app_group.as_dictionary().ok_or(Error::Parse)?;
let dict = app_group
.as_dictionary()
.ok_or(Error::Parse("applicationGroup".to_string()))?;
let application_group = dict
.get("applicationGroup")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("applicationGroup".to_string()))?
.to_string();
let name = dict
.get("name")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("name".to_string()))?
.to_string();
let identifier = dict
.get("identifier")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("identifier".to_string()))?
.to_string();
result.push(ApplicationGroup {
@@ -526,21 +536,21 @@ impl DeveloperSession {
let app_group_dict = response
.get("applicationGroup")
.and_then(|v| v.as_dictionary())
.ok_or(Error::Parse)?;
.ok_or(Error::Parse("applicationGroup".to_string()))?;
let application_group = app_group_dict
.get("applicationGroup")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("applicationGroup".to_string()))?
.to_string();
let name = app_group_dict
.get("name")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("name".to_string()))?
.to_string();
let identifier = app_group_dict
.get("identifier")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("identifier".to_string()))?
.to_string();
Ok(ApplicationGroup {
@@ -593,21 +603,21 @@ impl DeveloperSession {
let profile = response
.get("provisioningProfile")
.and_then(|v| v.as_dictionary())
.ok_or(Error::Parse)?;
.ok_or(Error::Parse("provisioningProfile".to_string()))?;
let name = profile
.get("name")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("name".to_string()))?
.to_string();
let provisioning_profile_id = profile
.get("provisioningProfileId")
.and_then(|v| v.as_string())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("provisioningProfileId".to_string()))?
.to_string();
let encoded_profile = profile
.get("encodedProfile")
.and_then(|v| v.as_data())
.ok_or(Error::Parse)?
.ok_or(Error::Parse("encodedProfile".to_string()))?
.to_vec();
Ok(ProvisioningProfile {

View File

@@ -10,12 +10,39 @@ pub use developer_session::{
DevelopmentCertificate, ListAppIdsResponse, ProvisioningProfile,
};
#[derive(Debug)]
use thiserror::Error as ThisError;
#[derive(Debug, Clone, ThisError)]
pub enum Error {
#[error("Authentication error {0}: {1}")]
Auth(i64, String),
#[error("Developer session error {0}: {1}")]
DeveloperSession(i64, String),
Generic,
Parse,
#[error("Error: {0}")]
Generic(String),
#[error("Failed to parse: {0}")]
Parse(String),
#[error("Invalid bundle: {0}")]
InvalidBundle(String),
#[error("Certificate error: {0}")]
Certificate(String),
#[error("Failed to use files: {0}")]
Filesystem(String),
}
pub trait SideloadLogger {
async fn log(&self, message: &str);
async fn error(&self, error: &Error);
}
pub struct DefaultLogger;
impl SideloadLogger for DefaultLogger {
async fn log(&self, message: &str) {
println!("{message}");
}
async fn error(&self, error: &Error) {
eprintln!("Error: {}", error);
}
}

View File

@@ -1,85 +1,81 @@
// This file was made using https://github.com/Dadoum/Sideloader as a reference.
use crate::Error;
use zsign_rust::ZSignOptions;
use crate::application::Application;
use crate::{Error, SideloadLogger};
use crate::{
certificate::CertificateIdentity,
developer_session::DeveloperDeviceType,
developer_session::{DeveloperDeviceType, DeveloperSession},
device::{DeviceInfo, install_app},
};
use std::{io::Write, path::PathBuf};
fn error_and_return(logger: &impl SideloadLogger, error: Error) -> Result<(), Error> {
logger.error(&error);
Err(error)
}
pub async fn sideload_app(
handle: &tauri::AppHandle,
window: &tauri::Window,
anisette_server: String,
device: DeviceInfo,
logger: impl SideloadLogger,
dev_session: &DeveloperSession,
device: &DeviceInfo,
app_path: PathBuf,
) -> Result<(), Error> {
if device.uuid.is_empty() {
return emit_error_and_return(window, "No device selected");
return error_and_return(&logger, Error::Generic("No device selected".to_string()));
}
let dev_session = match crate::sideloader::apple::get_developer_session(
&handle,
window,
anisette_server.clone(),
)
.await
{
Ok(acc) => acc,
Err(e) => {
return emit_error_and_return(
window,
&format!("Failed to login to Apple account: {:?}", e),
);
}
};
let team = match dev_session.get_team().await {
Ok(t) => t,
Err(e) => {
return emit_error_and_return(window, &format!("Failed to get team: {:?}", e));
return error_and_return(&logger, e);
}
};
window
.emit("build-output", "Successfully retrieved team".to_string())
.ok();
logger.log("Successfully retrieved team");
ensure_device_registered(&dev_session, window, &team, &device).await?;
let config_dir = handle.path().app_config_dir().map_err(|e| e.to_string())?;
let cert = match CertificateIdentity::new(config_dir, &dev_session, get_apple_email()).await {
Ok(c) => c,
Err(e) => {
return emit_error_and_return(window, &format!("Failed to get certificate: {:?}", e));
return error_and_return(&logger, e);
}
};
window
.emit(
"build-output",
"Certificate acquired succesfully".to_string(),
)
.ok();
logger.log("Successfully acquired certificate");
let mut list_app_id_response = match dev_session
.list_app_ids(DeveloperDeviceType::Ios, &team)
.await
{
Ok(ids) => ids,
Err(e) => {
return emit_error_and_return(window, &format!("Failed to list app IDs: {:?}", e));
return error_and_return(&logger, e);
}
};
let mut app = crate::sideloader::application::Application::new(app_path);
let mut app = Application::new(app_path);
let is_sidestore = app.bundle.bundle_identifier().unwrap_or("") == "com.SideStore.SideStore";
let main_app_bundle_id = match app.bundle.bundle_identifier() {
Some(id) => id.to_string(),
None => {
return emit_error_and_return(window, "No bundle identifier found in IPA");
return error_and_return(
&logger,
Error::InvalidBundle("No bundle identifier found in IPA".to_string()),
);
}
};
let main_app_id_str = format!("{}.{}", main_app_bundle_id, team.team_id);
let main_app_name = match app.bundle.bundle_name() {
Some(name) => name.to_string(),
None => {
return emit_error_and_return(window, "No bundle name found in IPA");
return error_and_return(
&logger,
Error::InvalidBundle("No bundle name found in IPA".to_string()),
);
}
};
@@ -88,13 +84,13 @@ pub async fn sideload_app(
for ext in extensions.iter_mut() {
if let Some(id) = ext.bundle_identifier() {
if !(id.starts_with(&main_app_bundle_id) && id.len() > main_app_bundle_id.len()) {
return emit_error_and_return(
window,
&format!(
return error_and_return(
&logger,
Error::InvalidBundle(format!(
"Extension {} is not part of the main app bundle identifier: {}",
ext.bundle_name().unwrap_or("Unknown"),
id
),
)),
);
} else {
ext.set_bundle_identifier(&format!(
@@ -123,13 +119,13 @@ pub async fn sideload_app(
.collect::<Vec<_>>();
if app_ids_to_register.len() > list_app_id_response.available_quantity.try_into().unwrap() {
return emit_error_and_return(
window,
&format!(
return error_and_return(
&logger,
Error::InvalidBundle(format!(
"This app requires {} app ids, but you only have {} available",
app_ids_to_register.len(),
list_app_id_response.available_quantity
),
)),
);
}
@@ -140,7 +136,7 @@ pub async fn sideload_app(
.add_app_id(DeveloperDeviceType::Ios, &team, &name, &id)
.await
{
return emit_error_and_return(window, &format!("Failed to register app ID: {:?}", e));
return error_and_return(&logger, e);
}
}
list_app_id_response = match dev_session
@@ -149,7 +145,7 @@ pub async fn sideload_app(
{
Ok(ids) => ids,
Err(e) => {
return emit_error_and_return(window, &format!("Failed to list app IDs: {:?}", e));
return error_and_return(&logger, e);
}
};
@@ -169,19 +165,17 @@ pub async fn sideload_app(
{
Some(id) => id,
None => {
return emit_error_and_return(
window,
&format!(
return error_and_return(
&logger,
Error::Generic(format!(
"Main app ID {} not found in registered app IDs",
main_app_id_str
),
)),
);
}
};
window
.emit("build-output", "Registered app IDs".to_string())
.ok();
logger.log("Successfully registered app IDs");
for app_id in app_ids.iter_mut() {
let app_group_feature_enabled = app_id
@@ -190,7 +184,9 @@ pub async fn sideload_app(
"APG3427HIY", /* Gotta love apple and their magic strings! */
)
.and_then(|v| v.as_boolean())
.ok_or("App group feature not found in app id")?;
.ok_or(Error::Generic(
"App group feature not found in app id".to_string(),
))?;
if !app_group_feature_enabled {
let mut body = plist::Dictionary::new();
body.insert("APG3427HIY".to_string(), plist::Value::Boolean(true));
@@ -200,10 +196,7 @@ pub async fn sideload_app(
{
Ok(new_feats) => new_feats,
Err(e) => {
return emit_error_and_return(
window,
&format!("Failed to update app ID features: {:?}", e),
);
return error_and_return(&logger, e);
}
};
app_id.features = new_features;
@@ -225,7 +218,7 @@ pub async fn sideload_app(
{
Ok(groups) => groups,
Err(e) => {
return emit_error_and_return(window, &format!("Failed to list app groups: {:?}", e));
return error_and_return(&logger, e);
}
};
@@ -246,10 +239,7 @@ pub async fn sideload_app(
{
Ok(group) => group,
Err(e) => {
return emit_error_and_return(
window,
&format!("Failed to register app group: {:?}", e),
);
return error_and_return(&logger, e);
}
}
} else {
@@ -267,13 +257,7 @@ pub async fn sideload_app(
)
.await;
if assign_res.is_err() {
return emit_error_and_return(
window,
&format!(
"Failed to assign app group to app ID: {:?}",
assign_res.err()
),
);
return error_and_return(&logger, assign_res.err().unwrap());
}
// let provisioning_profile = match account
// // This doesn't seem right to me, but it's what Sideloader does... Shouldn't it be downloading the provisioning profile for this app ID, not the main?
@@ -291,9 +275,7 @@ pub async fn sideload_app(
// provisioning_profiles.insert(app_id.identifier.clone(), provisioning_profile);
}
window
.emit("build-output", "Registered app groups".to_string())
.ok();
logger.log("Successfully registered app groups");
let provisioning_profile = match dev_session
.download_team_provisioning_profile(DeveloperDeviceType::Ios, &team, &main_app_id)
@@ -301,10 +283,7 @@ pub async fn sideload_app(
{
Ok(pp /* tee hee */) => pp,
Err(e) => {
return emit_error_and_return(
window,
&format!("Failed to download provisioning profile: {:?}", e),
);
return error_and_return(&logger, e);
}
};
@@ -318,9 +297,10 @@ pub async fn sideload_app(
std::fs::remove_file(&profile_path).map_err(|e| e.to_string())?;
}
let mut file = std::fs::File::create(&profile_path).map_err(|e| e.to_string())?;
let mut file =
std::fs::File::create(&profile_path).map_err(|e| Error::Filesystem(e.to_string()))?;
file.write_all(&provisioning_profile.encoded_profile)
.map_err(|e| e.to_string())?;
.map_err(|e| Error::Filesystem(e.to_string()))?;
// Without this, zsign complains it can't find the provision file
#[cfg(target_os = "windows")]
@@ -332,67 +312,28 @@ pub async fn sideload_app(
// TODO: Recursive for sub-bundles?
app.bundle.write_info().map_err(|e| e.to_string())?;
window
.emit("build-output", "Signining app...".to_string())
.ok();
let zsign_command = handle.shell().sidecar("zsign").unwrap().args([
"-k",
cert.get_private_key_file_path().to_str().unwrap(),
"-c",
cert.get_certificate_file_path().to_str().unwrap(),
"-m",
profile_path.to_str().unwrap(),
app.bundle.bundle_dir.to_str().unwrap(),
]);
let (mut rx, mut _child) = zsign_command.spawn().expect("Failed to spawn zsign");
let mut signing_failed = false;
while let Some(event) = rx.recv().await {
match event {
CommandEvent::Stdout(line_bytes) | CommandEvent::Stderr(line_bytes) => {
let line = String::from_utf8_lossy(&line_bytes);
window
.emit("build-output", Some(line))
.expect("failed to emit event");
}
CommandEvent::Terminated(result) => {
if result.code != Some(0) {
window
.emit("build-output", "App signing failed!".to_string())
.ok();
signing_failed = true;
break;
}
window.emit("build-output", "App signed!").ok();
window
.emit(
"build-output",
"Installing app (Transfer)... 0%".to_string(),
)
.ok();
let res = install_app(&device, &app.bundle.bundle_dir, |percentage| {
window
.emit("build-output", format!("Installing app... {}%", percentage))
.expect("failed to emit event");
})
.await;
if let Err(e) = res {
window
.emit("build-output", format!("Failed to install app: {:?}", e))
.ok();
signing_failed = true;
}
break;
}
_ => {}
match ZSignOptions::new(app.bundle.bundle_dir.to_str().unwrap())
.with_cert_file(cert.get_certificate_file_path().to_str().unwrap())
.with_pkey_file(cert.get_private_key_file_path().to_str().unwrap())
.with_prov_file(profile_path.to_str().unwrap())
.sign()
{
Ok(_) => {}
Err(e) => {
return error_and_return(&logger, &format!("Failed to sign app: {:?}", e));
}
}
};
if signing_failed {
return Err("Signing or installation failed".to_string());
logger.log("App signed!");
logger.log("Installing app (Transfer)... 0%");
let res = install_app(&device, &app.bundle.bundle_dir, |percentage| {
logger.log(format!("Installing app... {}%", percentage));
})
.await;
if let Err(e) = res {
return error_and_return(&logger, &format!("Failed to install app: {:?}", e));
}
Ok(())