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

View File

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

View File

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

View File

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

View File

@@ -15,11 +15,7 @@ impl AnisetteData {
let mut b = AnisetteHeaders::get_anisette_headers_provider(config.clone())?; let mut b = AnisetteHeaders::get_anisette_headers_provider(config.clone())?;
let base_headers = b.provider.get_authentication_headers().await?; let base_headers = b.provider.get_authentication_headers().await?;
Ok(AnisetteData { Ok(AnisetteData { base_headers, generated_at: SystemTime::now(), config })
base_headers,
generated_at: SystemTime::now(),
config,
})
} }
pub fn needs_refresh(&self) -> bool { pub fn needs_refresh(&self) -> bool {
@@ -69,7 +65,7 @@ impl AnisetteData {
"X-Apple-App-Info".to_owned(), "X-Apple-App-Info".to_owned(),
"com.apple.gs.xcode.auth".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 { if cpd {

View File

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

View File

@@ -1,8 +1,5 @@
// use icloud_auth::ani use base64::engine::{general_purpose, Engine};
use std::sync::Arc;
use num_bigint::BigUint; use num_bigint::BigUint;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use srp::{ use srp::{
client::{SrpClient, SrpClientVerifier}, client::{SrpClient, SrpClientVerifier},
@@ -11,15 +8,18 @@ use srp::{
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[test] #[test]
fn auth_debug() { fn auth_debug() {
// not a real account // 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 username = "apple3@f1sh.me";
let password = "WaffleTest123"; let password = "WaffleTest123";
let salt = base64::decode("6fK6ailLUcp2kJswJVrKjQ==").unwrap(); let salt = general_purpose::STANDARD
.decode("6fK6ailLUcp2kJswJVrKjQ==")
.unwrap();
let iters = 20832; let iters = 20832;
let mut password_hasher = sha2::Sha256::new(); let mut password_hasher = sha2::Sha256::new();
@@ -41,7 +41,9 @@ mod tests {
// apub: N2XHuh/4P1urPoBvDocF0RCRIl2pliZYqg9p6wGH0nnJdckJPn3M00jEqoM4teqH03HjG1murdcZiNHb5YayufW//+asW01XB7nYIIVvGiUFLRypYITEKYWBQ6h2q02GaZspYJKy98V8Fwcvr0ri+al7zJo1X1aoRKINyjV5TywhhwmTleI1qJkf+JBRYKKqO1XFtOTpQsysWD3ZJdK3K78kSgT3q0kXE3oDRMiHPAO77GFJZErYTuvI6QPRbOgcrn+RKV6AsjR5tUQAoSGRdtibdZTAQijJg788qVg+OFVCNZoY9GYVxa+Ze1bPGdkkgCYicTE8iNFG9KlJ+QpKgQ== // 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 client = SrpClient::<Sha256>::new(&G_2048);
let a_pub_compute = let a_pub_compute =
@@ -49,14 +51,21 @@ mod tests {
// expect it to be same to a_pub // expect it to be same to a_pub
println!( println!(
"compute a_pub: {:?}", "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(); let b_pub = general_purpose::STANDARD.decode("HlWxsRmNi/9DCGxYCoqCTfdSvpbx3mrgFLQfOsgf3Rojn7MQQN/g63PwlBghUcVVB4//yAaRRnz/VIByl8thA9AKuVZl8k52PAHKSh4e7TuXSeYCFr0+GYu8/hFdMDl42219uzSuOXuaKGVKq6hxEAf3n3uXXgQRkXWtLFJ5nn1wq/emf46hYAHzc/pYyvckAdh9WDCw95IXbzKD8LcPw/0ZQoydMuXgW2ZKZ52fiyEs94IZ7L5RLL7jY1nVdwtsp2fxeqiZ3DNmVZ2GdNrbJGT//160tyd2evtUtehr8ygXNzjWdjV0cc4+1F38ywSPFyieVzVTYzDywRllgo3A5A==").unwrap();
println!("fixed b_pub: {:?}", base64::encode(&b_pub)); println!(
"fixed b_pub: {:?}",
general_purpose::STANDARD.encode(&b_pub)
);
println!(""); 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( let verifier: SrpClientVerifier<Sha256> = SrpClient::<Sha256>::process_reply(
&client, &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"] remote-anisette-v3 = ["async", "dep:serde", "dep:serde_json", "dep:tokio-tungstenite", "dep:futures-util", "dep:chrono"]
[dependencies] [dependencies]
base64 = "0.21" base64 = "0.22"
hex = "0.4.3" hex = "0.4"
plist = "1.4" plist = "1.4"
reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "rustls-tls", "gzip"] } reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "rustls-tls", "gzip"] }
rand = "0.8" rand = "0.9"
sha2 = "0.10.8" sha2 = "0.10"
uuid = { version = "1.3", features = [ "v4", "fast-rng", "macro-diagnostics" ] } uuid = { version = "1.3", features = [ "v4", "fast-rng", "macro-diagnostics" ] }
android-loader = { git = "https://github.com/Dadoum/android-loader", branch = "bigger_pages" } android-loader = { git = "https://github.com/Dadoum/android-loader", branch = "bigger_pages" }
libc = "0.2" libc = "0.2"
@@ -23,11 +23,11 @@ log = "0.4"
async-trait = { version = "0.1", optional = true } async-trait = { version = "0.1", optional = true }
remove-async-await = { version = "1.0", optional = true } remove-async-await = { version = "1.0", optional = true }
serde = { version = "1.0", features = ["derive"], optional = true } serde = { version = "1.0", features = ["derive"], optional = true }
serde_json = { version = "1.0.142", optional = true } serde_json = { version = "1.0.115", optional = true }
tokio-tungstenite = { version = "0.20.1", optional = true, features = ["rustls-tls-webpki-roots"] } tokio-tungstenite = { version = "0.27.0", optional = true, features = ["rustls-tls-webpki-roots"] }
futures-util = { version = "0.3.28", optional = true } futures-util = { version = "0.3.28", optional = true }
chrono = { version = "0.4.37", optional = true } chrono = { version = "0.4.37", optional = true }
thiserror = "1.0.58" thiserror = "2"
anyhow = "1.0.81" anyhow = "1.0.81"
[target.'cfg(target_os = "macos")'.dependencies] [target.'cfg(target_os = "macos")'.dependencies]

View File

@@ -46,7 +46,7 @@ pub enum ADIError {
ReqwestError(#[from] reqwest::Error), ReqwestError(#[from] reqwest::Error),
Base64Error(#[from] base64::DecodeError), Base64Error(#[from] base64::DecodeError),
InvalidHeaderValue(#[from] InvalidHeaderValue), InvalidHeaderValue(#[from] InvalidHeaderValue),
IOError(#[from] io::Error) IOError(#[from] io::Error),
} }
impl ADIError { impl ADIError {
@@ -291,7 +291,9 @@ pub struct ADIProxyAnisetteProvider<ProxyType: ADIProxy + 'static> {
impl<ProxyType: ADIProxy + 'static> ADIProxyAnisetteProvider<ProxyType> { impl<ProxyType: ADIProxy + 'static> ADIProxyAnisetteProvider<ProxyType> {
/// If you use this method, you are expected to set the identifier yourself. /// 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 }) Ok(ADIProxyAnisetteProvider { adi_proxy })
} }
@@ -309,7 +311,7 @@ impl<ProxyType: ADIProxy + 'static> ADIProxyAnisetteProvider<ProxyType> {
if identifier_file.metadata()?.len() == IDENTIFIER_LENGTH as u64 { if identifier_file.metadata()?.len() == IDENTIFIER_LENGTH as u64 {
identifier_file.read_exact(&mut identifier)?; identifier_file.read_exact(&mut identifier)?;
} else { } else {
rand::thread_rng().fill_bytes(&mut identifier); rand::rng().fill_bytes(&mut identifier);
identifier_file.write_all(&identifier)?; identifier_file.write_all(&identifier)?;
} }

View File

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

View File

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

View File

@@ -66,11 +66,16 @@ pub struct StoreServicesCoreADIProxy<'lt> {
} }
impl StoreServicesCoreADIProxy<'_> { 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) 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. // Should be safe if the library is correct.
unsafe { unsafe {
LoaderHelpers::setup_hooks(); LoaderHelpers::setup_hooks();
@@ -104,12 +109,8 @@ impl StoreServicesCoreADIProxy<'_> {
.ok_or(AnisetteError::InvalidLibraryFormat)?, .ok_or(AnisetteError::InvalidLibraryFormat)?,
); );
let path = CString::new( let path =
native_library_path CString::new(native_library_path.to_str().ok_or(AnisetteError::Misc)?).unwrap();
.to_str()
.ok_or(AnisetteError::Misc)?,
)
.unwrap();
assert_eq!((adi_load_library_with_path)(path.as_ptr() as *const u8), 0); assert_eq!((adi_load_library_with_path)(path.as_ptr() as *const u8), 0);
let adi_set_android_id = store_services_core let adi_set_android_id = store_services_core
@@ -163,9 +164,7 @@ impl StoreServicesCoreADIProxy<'_> {
adi_otp_request: std::mem::transmute(adi_otp_request), adi_otp_request: std::mem::transmute(adi_otp_request),
}; };
proxy.set_provisioning_path( proxy.set_provisioning_path(provisioning_path.to_str().ok_or(AnisetteError::Misc)?)?;
provisioning_path.to_str().ok_or(AnisetteError::Misc)?,
)?;
Ok(proxy) Ok(proxy)
} }
@@ -370,7 +369,7 @@ unsafe fn __errno_location() -> *mut i32 {
#[sysv64] #[sysv64]
fn arc4random() -> u32 { fn arc4random() -> u32 {
rand::thread_rng().gen() rand::rng().random()
} }
#[sysv64] #[sysv64]
@@ -412,10 +411,10 @@ impl LoaderHelpers {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::AnisetteError;
use crate::{AnisetteConfiguration, AnisetteHeaders}; use crate::{AnisetteConfiguration, AnisetteHeaders};
use log::info; use log::info;
use std::path::PathBuf; use std::path::PathBuf;
use crate::AnisetteError;
#[cfg(not(feature = "async"))] #[cfg(not(feature = "async"))]
#[test] #[test]
@@ -436,7 +435,6 @@ mod tests {
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[tokio::test] #[tokio::test]
async fn fetch_anisette_ssc_async() -> Result<(), AnisetteError> { async fn fetch_anisette_ssc_async() -> Result<(), AnisetteError> {
crate::tests::init_logger(); crate::tests::init_logger();
let mut provider = AnisetteHeaders::get_ssc_anisette_headers_provider( 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 hash_string = hex::encode(hasher.finalize()).to_lowercase();
let key_path = configuration_path.join("keys").join(hash_string); let key_path = configuration_path.join("keys").join(hash_string);
fs::create_dir_all(&key_path) 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 key_file = key_path.join("key.pem");
let cert_file = key_path.join("cert.pem"); let cert_file = key_path.join("cert.pem");
@@ -54,7 +54,7 @@ impl CertificateIdentity {
.private_key_to_pem_pkcs8() .private_key_to_pem_pkcs8()
.map_err(|e| Error::Certificate(format!("Failed to encode private key: {}", e)))?; .map_err(|e| Error::Certificate(format!("Failed to encode private key: {}", e)))?;
fs::write(&key_file, pem_data) 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 key
}; };
@@ -75,7 +75,7 @@ impl CertificateIdentity {
Error::Certificate(format!("Failed to encode certificate to PEM: {}", e)) Error::Certificate(format!("Failed to encode certificate to PEM: {}", e))
})?; })?;
fs::write(&cert_identity.cert_file, cert_pem).map_err(|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); return Ok(cert_identity);
@@ -200,7 +200,7 @@ impl CertificateIdentity {
Error::Certificate(format!("Failed to encode certificate to PEM: {}", e)) Error::Certificate(format!("Failed to encode certificate to PEM: {}", e))
})?; })?;
fs::write(&self.cert_file, cert_pem) 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); self.certificate = Some(certificate);

View File

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

View File

@@ -10,12 +10,39 @@ pub use developer_session::{
DevelopmentCertificate, ListAppIdsResponse, ProvisioningProfile, DevelopmentCertificate, ListAppIdsResponse, ProvisioningProfile,
}; };
#[derive(Debug)] use thiserror::Error as ThisError;
#[derive(Debug, Clone, ThisError)]
pub enum Error { pub enum Error {
#[error("Authentication error {0}: {1}")]
Auth(i64, String), Auth(i64, String),
#[error("Developer session error {0}: {1}")]
DeveloperSession(i64, String), DeveloperSession(i64, String),
Generic, #[error("Error: {0}")]
Parse, Generic(String),
#[error("Failed to parse: {0}")]
Parse(String),
#[error("Invalid bundle: {0}")]
InvalidBundle(String), InvalidBundle(String),
#[error("Certificate error: {0}")]
Certificate(String), 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. // 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::{ use crate::{
certificate::CertificateIdentity, certificate::CertificateIdentity,
developer_session::DeveloperDeviceType, developer_session::{DeveloperDeviceType, DeveloperSession},
device::{DeviceInfo, install_app}, device::{DeviceInfo, install_app},
}; };
use std::{io::Write, path::PathBuf}; 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( pub async fn sideload_app(
handle: &tauri::AppHandle, logger: impl SideloadLogger,
window: &tauri::Window, dev_session: &DeveloperSession,
anisette_server: String, device: &DeviceInfo,
device: DeviceInfo,
app_path: PathBuf, app_path: PathBuf,
) -> Result<(), Error> { ) -> Result<(), Error> {
if device.uuid.is_empty() { 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 { let team = match dev_session.get_team().await {
Ok(t) => t, Ok(t) => t,
Err(e) => { 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()) logger.log("Successfully retrieved team");
.ok();
ensure_device_registered(&dev_session, window, &team, &device).await?; ensure_device_registered(&dev_session, window, &team, &device).await?;
let config_dir = handle.path().app_config_dir().map_err(|e| e.to_string())?; 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 { let cert = match CertificateIdentity::new(config_dir, &dev_session, get_apple_email()).await {
Ok(c) => c, Ok(c) => c,
Err(e) => { Err(e) => {
return emit_error_and_return(window, &format!("Failed to get certificate: {:?}", e)); return error_and_return(&logger, e);
} }
}; };
window
.emit( logger.log("Successfully acquired certificate");
"build-output",
"Certificate acquired succesfully".to_string(),
)
.ok();
let mut list_app_id_response = match dev_session let mut list_app_id_response = match dev_session
.list_app_ids(DeveloperDeviceType::Ios, &team) .list_app_ids(DeveloperDeviceType::Ios, &team)
.await .await
{ {
Ok(ids) => ids, Ok(ids) => ids,
Err(e) => { 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 is_sidestore = app.bundle.bundle_identifier().unwrap_or("") == "com.SideStore.SideStore";
let main_app_bundle_id = match app.bundle.bundle_identifier() { let main_app_bundle_id = match app.bundle.bundle_identifier() {
Some(id) => id.to_string(), Some(id) => id.to_string(),
None => { 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_id_str = format!("{}.{}", main_app_bundle_id, team.team_id);
let main_app_name = match app.bundle.bundle_name() { let main_app_name = match app.bundle.bundle_name() {
Some(name) => name.to_string(), Some(name) => name.to_string(),
None => { 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() { for ext in extensions.iter_mut() {
if let Some(id) = ext.bundle_identifier() { if let Some(id) = ext.bundle_identifier() {
if !(id.starts_with(&main_app_bundle_id) && id.len() > main_app_bundle_id.len()) { if !(id.starts_with(&main_app_bundle_id) && id.len() > main_app_bundle_id.len()) {
return emit_error_and_return( return error_and_return(
window, &logger,
&format!( Error::InvalidBundle(format!(
"Extension {} is not part of the main app bundle identifier: {}", "Extension {} is not part of the main app bundle identifier: {}",
ext.bundle_name().unwrap_or("Unknown"), ext.bundle_name().unwrap_or("Unknown"),
id id
), )),
); );
} else { } else {
ext.set_bundle_identifier(&format!( ext.set_bundle_identifier(&format!(
@@ -123,13 +119,13 @@ pub async fn sideload_app(
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if app_ids_to_register.len() > list_app_id_response.available_quantity.try_into().unwrap() { if app_ids_to_register.len() > list_app_id_response.available_quantity.try_into().unwrap() {
return emit_error_and_return( return error_and_return(
window, &logger,
&format!( Error::InvalidBundle(format!(
"This app requires {} app ids, but you only have {} available", "This app requires {} app ids, but you only have {} available",
app_ids_to_register.len(), app_ids_to_register.len(),
list_app_id_response.available_quantity list_app_id_response.available_quantity
), )),
); );
} }
@@ -140,7 +136,7 @@ pub async fn sideload_app(
.add_app_id(DeveloperDeviceType::Ios, &team, &name, &id) .add_app_id(DeveloperDeviceType::Ios, &team, &name, &id)
.await .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 list_app_id_response = match dev_session
@@ -149,7 +145,7 @@ pub async fn sideload_app(
{ {
Ok(ids) => ids, Ok(ids) => ids,
Err(e) => { 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, Some(id) => id,
None => { None => {
return emit_error_and_return( return error_and_return(
window, &logger,
&format!( Error::Generic(format!(
"Main app ID {} not found in registered app IDs", "Main app ID {} not found in registered app IDs",
main_app_id_str main_app_id_str
), )),
); );
} }
}; };
window logger.log("Successfully registered app IDs");
.emit("build-output", "Registered app IDs".to_string())
.ok();
for app_id in app_ids.iter_mut() { for app_id in app_ids.iter_mut() {
let app_group_feature_enabled = app_id let app_group_feature_enabled = app_id
@@ -190,7 +184,9 @@ pub async fn sideload_app(
"APG3427HIY", /* Gotta love apple and their magic strings! */ "APG3427HIY", /* Gotta love apple and their magic strings! */
) )
.and_then(|v| v.as_boolean()) .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 { if !app_group_feature_enabled {
let mut body = plist::Dictionary::new(); let mut body = plist::Dictionary::new();
body.insert("APG3427HIY".to_string(), plist::Value::Boolean(true)); body.insert("APG3427HIY".to_string(), plist::Value::Boolean(true));
@@ -200,10 +196,7 @@ pub async fn sideload_app(
{ {
Ok(new_feats) => new_feats, Ok(new_feats) => new_feats,
Err(e) => { Err(e) => {
return emit_error_and_return( return error_and_return(&logger, e);
window,
&format!("Failed to update app ID features: {:?}", e),
);
} }
}; };
app_id.features = new_features; app_id.features = new_features;
@@ -225,7 +218,7 @@ pub async fn sideload_app(
{ {
Ok(groups) => groups, Ok(groups) => groups,
Err(e) => { 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, Ok(group) => group,
Err(e) => { Err(e) => {
return emit_error_and_return( return error_and_return(&logger, e);
window,
&format!("Failed to register app group: {:?}", e),
);
} }
} }
} else { } else {
@@ -267,13 +257,7 @@ pub async fn sideload_app(
) )
.await; .await;
if assign_res.is_err() { if assign_res.is_err() {
return emit_error_and_return( return error_and_return(&logger, assign_res.err().unwrap());
window,
&format!(
"Failed to assign app group to app ID: {:?}",
assign_res.err()
),
);
} }
// let provisioning_profile = match account // 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? // // 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); // provisioning_profiles.insert(app_id.identifier.clone(), provisioning_profile);
} }
window logger.log("Successfully registered app groups");
.emit("build-output", "Registered app groups".to_string())
.ok();
let provisioning_profile = match dev_session let provisioning_profile = match dev_session
.download_team_provisioning_profile(DeveloperDeviceType::Ios, &team, &main_app_id) .download_team_provisioning_profile(DeveloperDeviceType::Ios, &team, &main_app_id)
@@ -301,10 +283,7 @@ pub async fn sideload_app(
{ {
Ok(pp /* tee hee */) => pp, Ok(pp /* tee hee */) => pp,
Err(e) => { Err(e) => {
return emit_error_and_return( return error_and_return(&logger, e);
window,
&format!("Failed to download provisioning profile: {:?}", e),
);
} }
}; };
@@ -318,9 +297,10 @@ pub async fn sideload_app(
std::fs::remove_file(&profile_path).map_err(|e| e.to_string())?; 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) 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 // Without this, zsign complains it can't find the provision file
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
@@ -332,67 +312,28 @@ pub async fn sideload_app(
// TODO: Recursive for sub-bundles? // TODO: Recursive for sub-bundles?
app.bundle.write_info().map_err(|e| e.to_string())?; app.bundle.write_info().map_err(|e| e.to_string())?;
window match ZSignOptions::new(app.bundle.bundle_dir.to_str().unwrap())
.emit("build-output", "Signining app...".to_string()) .with_cert_file(cert.get_certificate_file_path().to_str().unwrap())
.ok(); .with_pkey_file(cert.get_private_key_file_path().to_str().unwrap())
.with_prov_file(profile_path.to_str().unwrap())
let zsign_command = handle.shell().sidecar("zsign").unwrap().args([ .sign()
"-k", {
cert.get_private_key_file_path().to_str().unwrap(), Ok(_) => {}
"-c", Err(e) => {
cert.get_certificate_file_path().to_str().unwrap(), return error_and_return(&logger, &format!("Failed to sign app: {:?}", e));
"-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;
}
_ => {}
} }
} };
if signing_failed { logger.log("App signed!");
return Err("Signing or installation failed".to_string());
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(()) Ok(())