From 92085d878bfb19e9e98624aa09dd72659ca4b023 Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 21 Nov 2025 00:10:15 -0500 Subject: [PATCH 01/71] Start switching to apple-codesign --- Cargo.lock | 2938 +++++++++++++++++++++++++++++++++- README.md | 2 +- examples/minimal/Cargo.toml | 2 +- isideload/Cargo.toml | 11 +- isideload/src/certificate.rs | 192 ++- isideload/src/lib.rs | 4 +- isideload/src/sideload.rs | 39 +- 7 files changed, 3007 insertions(+), 181 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 27fb430..4bacf27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,6 +43,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -52,6 +64,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -61,12 +79,226 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + [[package]] name = "anyhow" version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "app-store-connect" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed88de4349fc3eb58529530edeb7177516c94712d3dc8bf11bd76ac1715695c4" +dependencies = [ + "anyhow", + "base64 0.22.1", + "clap", + "dirs", + "env_logger", + "jsonwebtoken", + "log", + "pem", + "rand 0.8.5", + "reqwest 0.12.24", + "rsa", + "serde", + "serde_json", + "thiserror 2.0.17", + "x509-certificate 0.24.0", +] + +[[package]] +name = "apple-bundles" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f40bb8f844cec39fa3aceae717808c2ac3d2b6c474a9dffbeba07a4a945d10" +dependencies = [ + "anyhow", + "plist", + "simple-file-manifest", + "walkdir", +] + +[[package]] +name = "apple-codesign" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f24e9ebdb70a2aee3ca1cea217009fb50776955f0d7678c31d22e48c1524667f" +dependencies = [ + "anyhow", + "app-store-connect", + "apple-bundles", + "apple-flat-package", + "apple-xar", + "aws-config", + "aws-sdk-s3", + "aws-smithy-http 0.60.12", + "aws-smithy-types", + "base64 0.22.1", + "bcder", + "bitflags 2.10.0", + "bytes", + "chrono", + "clap", + "cryptographic-message-syntax", + "der 0.7.10", + "dialoguer", + "difference", + "digest", + "dirs", + "elliptic-curve 0.13.8", + "env_logger", + "figment", + "filetime", + "glob", + "goblin", + "hex", + "log", + "md-5", + "minicbor", + "num-traits", + "object", + "oid-registry", + "once_cell", + "p12", + "p256 0.13.2", + "pem", + "pkcs1", + "pkcs8 0.10.2", + "plist", + "rand 0.8.5", + "rasn", + "rayon", + "regex", + "reqwest 0.12.24", + "ring", + "rsa", + "scroll", + "security-framework 2.11.1", + "security-framework-sys", + "semver", + "serde", + "serde_json", + "serde_yaml", + "sha2", + "signature 2.2.0", + "simple-file-manifest", + "spake2", + "spki 0.7.3", + "subtle", + "tempfile", + "thiserror 2.0.17", + "tokio", + "tungstenite 0.24.0", + "uuid", + "walkdir", + "widestring", + "windows-sys 0.59.0", + "x509", + "x509-certificate 0.24.0", + "xml-rs", + "yasna", + "zeroize", + "zip 2.4.2", + "zip_structs", +] + +[[package]] +name = "apple-flat-package" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9d5a1fd8af4a376cc33d7e816a13f8ce127d52101f5dbc8061fb595397bea0" +dependencies = [ + "apple-xar", + "cpio-archive", + "flate2", + "scroll", + "serde", + "serde-xml-rs", + "thiserror 2.0.17", +] + +[[package]] +name = "apple-xar" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9631e781df71ebd049d7b4988cdae88712324cb20eb127fd79026bc8f1335d93" +dependencies = [ + "base64 0.22.1", + "bcder", + "bzip2", + "chrono", + "cryptographic-message-syntax", + "digest", + "flate2", + "log", + "md-5", + "rand 0.8.5", + "reqwest 0.12.24", + "scroll", + "serde", + "serde-xml-rs", + "sha1", + "sha2", + "signature 2.2.0", + "thiserror 2.0.17", + "url", + "x509-certificate 0.24.0", + "xml-rs", + "xz2", +] + [[package]] name = "arbitrary" version = "1.4.2" @@ -76,6 +308,50 @@ dependencies = [ "derive_arbitrary", ] +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "asn1-rs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 1.0.69", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-compression" version = "0.4.32" @@ -111,17 +387,495 @@ dependencies = [ "crc32fast", "futures-lite", "pin-project", - "thiserror", + "thiserror 2.0.17", "tokio", "tokio-util", ] +[[package]] +name = "atomic" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "aws-config" +version = "1.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0149602eeaf915158e14029ba0c78dedb8c08d554b024d54c8f239aab46511d" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sdk-sso", + "aws-sdk-ssooidc", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-http 0.62.5", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "hex", + "http 1.3.1", + "ring", + "time", + "tokio", + "tracing", + "url", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "1.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b01c9521fa01558f750d183c8c68c81b0155b9d193a4ba7f84c36bd1b6d04a06" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "zeroize", +] + +[[package]] +name = "aws-lc-rs" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5932a7d9d28b0d2ea34c6b3779d35e3dd6f6345317c34e73438c4f1f29144151" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1826f2e4cfc2cd19ee53c42fbf68e2f81ec21108e0b7ecf6a71cf062137360fc" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "aws-runtime" +version = "1.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce527fb7e53ba9626fc47824f25e256250556c40d8f81d27dd92aa38239d632" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-eventstream", + "aws-smithy-http 0.62.5", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "http-body 0.4.6", + "percent-encoding", + "pin-project-lite", + "tracing", + "uuid", +] + +[[package]] +name = "aws-sdk-s3" +version = "1.115.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdaa0053cbcbc384443dd24569bd5d1664f86427b9dc04677bd0ab853954baec" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-checksums", + "aws-smithy-eventstream", + "aws-smithy-http 0.62.5", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "bytes", + "fastrand", + "hex", + "hmac", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "lru", + "percent-encoding", + "regex-lite", + "sha2", + "tracing", + "url", +] + +[[package]] +name = "aws-sdk-sso" +version = "1.90.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f18e53542c522459e757f81e274783a78f8c81acdfc8d1522ee8a18b5fb1c66" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http 0.62.5", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-ssooidc" +version = "1.92.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532f4d866012ffa724a4385c82e8dd0e59f0ca0e600f3f22d4c03b6824b34e4a" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http 0.62.5", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sts" +version = "1.94.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1be6fbbfa1a57724788853a623378223fe828fc4c09b146c992f0c95b6256174" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http 0.62.5", + "aws-smithy-json", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "fastrand", + "http 0.2.12", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "1.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c35452ec3f001e1f2f6db107b6373f1f48f05ec63ba2c5c9fa91f07dad32af11" +dependencies = [ + "aws-credential-types", + "aws-smithy-eventstream", + "aws-smithy-http 0.62.5", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "crypto-bigint 0.5.5", + "form_urlencoded", + "hex", + "hmac", + "http 0.2.12", + "http 1.3.1", + "p256 0.11.1", + "percent-encoding", + "ring", + "sha2", + "subtle", + "time", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-async" +version = "1.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "127fcfad33b7dfc531141fda7e1c402ac65f88aca5511a4d31e2e3d2cd01ce9c" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "aws-smithy-checksums" +version = "0.63.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95bd108f7b3563598e4dc7b62e1388c9982324a2abd622442167012690184591" +dependencies = [ + "aws-smithy-http 0.62.5", + "aws-smithy-types", + "bytes", + "crc-fast", + "hex", + "http 0.2.12", + "http-body 0.4.6", + "md-5", + "pin-project-lite", + "sha1", + "sha2", + "tracing", +] + +[[package]] +name = "aws-smithy-eventstream" +version = "0.60.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e29a304f8319781a39808847efb39561351b1bb76e933da7aa90232673638658" +dependencies = [ + "aws-smithy-types", + "bytes", + "crc32fast", +] + +[[package]] +name = "aws-smithy-http" +version = "0.60.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7809c27ad8da6a6a68c454e651d4962479e81472aa19ae99e59f9aba1f9713cc" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-http" +version = "0.62.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445d5d720c99eed0b4aa674ed00d835d9b1427dd73e04adaf2f94c6b2d6f9fca" +dependencies = [ + "aws-smithy-eventstream", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "futures-util", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-http-client" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "623254723e8dfd535f566ee7b2381645f8981da086b5c4aa26c0c41582bb1d2c" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "h2 0.3.27", + "h2 0.4.12", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper 1.8.1", + "hyper-rustls 0.24.2", + "hyper-rustls 0.27.7", + "hyper-util", + "pin-project-lite", + "rustls 0.21.12", + "rustls 0.23.35", + "rustls-native-certs 0.8.2", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.4", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.61.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2db31f727935fc63c6eeae8b37b438847639ec330a9161ece694efba257e0c54" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-observability" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d1881b1ea6d313f9890710d65c158bdab6fb08c91ea825f74c1c8c357baf4cc" +dependencies = [ + "aws-smithy-runtime-api", +] + +[[package]] +name = "aws-smithy-query" +version = "0.60.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d28a63441360c477465f80c7abac3b9c4d075ca638f982e605b7dc2a2c7156c9" +dependencies = [ + "aws-smithy-types", + "urlencoding", +] + +[[package]] +name = "aws-smithy-runtime" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bbe9d018d646b96c7be063dd07987849862b0e6d07c778aad7d93d1be6c1ef0" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http 0.62.5", + "aws-smithy-http-client", + "aws-smithy-observability", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "fastrand", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "http-body 1.0.1", + "pin-project-lite", + "pin-utils", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7204f9fd94749a7c53b26da1b961b4ac36bf070ef1e0b94bb09f79d4f6c193" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "bytes", + "http 0.2.12", + "http 1.3.1", + "pin-project-lite", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-types" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f535879a207fce0db74b679cfc3e91a3159c8144d717d55f5832aea9eef46e" +dependencies = [ + "base64-simd", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "http-body 1.0.1", + "http-body-util", + "itoa", + "num-integer", + "pin-project-lite", + "pin-utils", + "ryu", + "serde", + "time", + "tokio", + "tokio-util", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.60.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab77cdd036b11056d2a30a7af7b775789fb024bf216acc13884c6c97752ae56" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "1.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d79fb68e3d7fe5d4833ea34dc87d2e97d26d3086cb3da660bb6b1f76d98680b6" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "rustc_version", + "tracing", +] + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.21.7" @@ -134,12 +888,32 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + [[package]] name = "base64ct" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +[[package]] +name = "bcder" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f7c42c9913f68cf9390a225e81ad56a5c515347287eb98baa710090ca1de86d" +dependencies = [ + "bytes", + "smallvec", +] + [[package]] name = "bindgen" version = "0.72.1" @@ -172,6 +946,28 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "bitvec-nom2" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d988fcc40055ceaa85edc55875a08f8abd29018582647fd82ad6128dba14a5f0" +dependencies = [ + "bitvec", + "nom", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -196,12 +992,60 @@ version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +[[package]] +name = "bytemuck" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" + +[[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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + +[[package]] +name = "bytesize" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e93abca9e28e0a1b9877922aacb20576e05d4679ffa78c3d6dc22a26a216659" + +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.13+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "cbc" version = "0.1.2" @@ -238,6 +1082,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.42" @@ -273,6 +1123,61 @@ dependencies = [ "libloading", ] +[[package]] +name = "clap" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + [[package]] name = "compression-codecs" version = "0.4.31" @@ -290,12 +1195,43 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e47641d3deaf41fb1538ac1f54735925e275eaf3bf4d55c81b137fba797e5cbb" +[[package]] +name = "console" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width", + "windows-sys 0.59.0", +] + [[package]] name = "const-oid" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const_panic" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e262cdaac42494e3ae34c43969f9cdeb7da178bdb4b66fa6a1ea2edb4c8ae652" +dependencies = [ + "typewit", +] + +[[package]] +name = "cookie-factory" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" +dependencies = [ + "futures", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -306,12 +1242,34 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpio-archive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f11d34b07689c21889fc89bd7cc885b3244b0157bbededf4a1c159832cd0df05" +dependencies = [ + "chrono", + "is_executable", + "simple-file-manifest", + "thiserror 1.0.69", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -321,6 +1279,34 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crc-fast" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ddc2d09feefeee8bd78101665bd8645637828fa9317f9f292496dbbd8c65ff3" +dependencies = [ + "crc", + "digest", + "rand 0.9.2", + "regex", + "rustversion", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -330,6 +1316,55 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array 0.14.9", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array 0.14.9", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -341,6 +1376,23 @@ dependencies = [ "typenum", ] +[[package]] +name = "cryptographic-message-syntax" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a99e58d7755c646cb3f2a138d99f90da4c495282e1700b82daff8a48759ce0" +dependencies = [ + "bcder", + "bytes", + "chrono", + "hex", + "pem", + "reqwest 0.12.24", + "ring", + "signature 2.2.0", + "x509-certificate 0.24.0", +] + [[package]] name = "ctr" version = "0.9.2" @@ -350,12 +1402,48 @@ dependencies = [ "cipher", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "rand_core 0.6.4", + "rustc_version", + "subtle", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "data-encoding" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "der" version = "0.7.10" @@ -365,6 +1453,7 @@ dependencies = [ "const-oid", "der_derive", "flagset", + "pem-rfc7468", "zeroize", ] @@ -399,6 +1488,34 @@ dependencies = [ "syn", ] +[[package]] +name = "des" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" +dependencies = [ + "cipher", +] + +[[package]] +name = "dialoguer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" +dependencies = [ + "console", + "shell-words", + "tempfile", + "thiserror 1.0.69", + "zeroize", +] + +[[package]] +name = "difference" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" + [[package]] name = "digest" version = "0.10.7" @@ -406,10 +1523,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", "subtle", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -421,12 +1560,88 @@ dependencies = [ "syn", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979", + "signature 1.6.4", +] + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der 0.7.10", + "digest", + "elliptic-curve 0.13.8", + "signature 2.2.0", + "spki 0.7.3", +] + [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest", + "ff 0.12.1", + "generic-array 0.14.9", + "group 0.12.1", + "pkcs8 0.9.0", + "rand_core 0.6.4", + "sec1 0.3.0", + "subtle", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.5", + "digest", + "ff 0.13.1", + "generic-array 0.14.9", + "group 0.13.0", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "sec1 0.7.3", + "subtle", + "zeroize", +] + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "encoding_rs" version = "0.8.35" @@ -436,6 +1651,29 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_filter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -458,6 +1696,58 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "figment" +version = "0.10.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" +dependencies = [ + "atomic", + "pear", + "serde", + "toml", + "uncased", + "version_check", +] + +[[package]] +name = "filetime" +version = "0.2.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.60.2", +] + [[package]] name = "find-msvc-tools" version = "0.1.4" @@ -487,6 +1777,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foreign-types" version = "0.3.2" @@ -511,6 +1807,18 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.31" @@ -621,6 +1929,7 @@ checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -640,8 +1949,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -651,9 +1962,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasip2", + "wasm-bindgen", ] [[package]] @@ -672,6 +1985,39 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "goblin" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daa0a64d21a7eb230583b4c5f4e23b7e4e57974f96620f42a7e75e08ae66d745" +dependencies = [ + "log", + "plain", + "scroll", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.1", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "h2" version = "0.3.27" @@ -691,18 +2037,72 @@ dependencies = [ "tracing", ] +[[package]] +name = "h2" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.3.1", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + [[package]] name = "hashbrown" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -745,6 +2145,29 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.3.1", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http 1.3.1", + "http-body 1.0.1", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.10.1" @@ -767,9 +2190,9 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.27", "http 0.2.12", - "http-body", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -781,6 +2204,28 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2 0.4.12", + "http 1.3.1", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + [[package]] name = "hyper-rustls" version = "0.24.2" @@ -789,12 +2234,32 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper", + "hyper 0.14.32", + "log", "rustls 0.21.12", + "rustls-native-certs 0.6.3", "tokio", "tokio-rustls 0.24.1", ] +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http 1.3.1", + "hyper 1.8.1", + "hyper-util", + "rustls 0.23.35", + "rustls-native-certs 0.8.2", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.4", + "tower-service", + "webpki-roots 1.0.4", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -802,12 +2267,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper", + "hyper 0.14.32", "native-tls", "tokio", "tokio-native-tls", ] +[[package]] +name = "hyper-util" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "hyper 1.8.1", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2 0.6.1", + "tokio", + "tower-service", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.64" @@ -926,7 +2415,7 @@ dependencies = [ "plist", "rustls 0.23.35", "serde", - "thiserror", + "thiserror 2.0.17", "tokio", "tokio-rustls 0.26.4", "tracing", @@ -960,9 +2449,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.16.0", ] +[[package]] +name = "inlinable_string" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" + [[package]] name = "inout" version = "0.1.4" @@ -979,21 +2474,52 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "iri-string" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is_executable" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baabb8b4867b26294d818bf3f651a454b6901431711abb96e296245888d6e8c4" +dependencies = [ + "windows-sys 0.60.2", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "isideload" version = "0.1.21" dependencies = [ + "apple-codesign", + "der 0.7.10", "hex", "idevice", "nab138_icloud_auth", - "openssl", + "p12", + "pkcs8 0.10.2", "plist", + "rand 0.8.5", + "rcgen", + "rsa", "serde", "sha1", - "thiserror", + "thiserror 2.0.17", "uuid", - "zip", - "zsign-rust", + "x509-certificate 0.25.0", + "zip 4.6.1", ] [[package]] @@ -1011,6 +2537,30 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jiff" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "jobserver" version = "0.1.34" @@ -1031,11 +2581,49 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonwebtoken" +version = "9.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" +dependencies = [ + "base64 0.22.1", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + +[[package]] +name = "konst" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4381b9b00c55f251f2ebe9473aef7c117e96828def1a7cb3bd3f0f903c6894e9" +dependencies = [ + "const_panic", + "konst_kernel", + "typewit", +] + +[[package]] +name = "konst_kernel" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4b1eb7788f3824c629b1116a7a9060d6e898c358ebff59070093d51103dcc3c" +dependencies = [ + "typewit", +] + [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -1053,6 +2641,23 @@ dependencies = [ "windows-link", ] +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "libredox" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +dependencies = [ + "bitflags 2.10.0", + "libc", + "redox_syscall", +] + [[package]] name = "libz-rs-sys" version = "0.5.2" @@ -1080,6 +2685,42 @@ version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.5", +] + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "lzma-sys" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + [[package]] name = "memchr" version = "2.7.6" @@ -1092,6 +2733,26 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minicbor" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0452a60c1863c1f50b5f77cd295e8d2786849f35883f0b9e18e7e6e1b5691b0" +dependencies = [ + "minicbor-derive", +] + +[[package]] +name = "minicbor-derive" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd2209fff77f705b00c737016a48e73733d7fbccb8b007194db148f03561fb70" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "minimal" version = "0.1.0" @@ -1145,14 +2806,14 @@ dependencies = [ "pbkdf2", "pkcs7", "plist", - "rand", - "reqwest", + "rand 0.9.2", + "reqwest 0.11.27", "rustls 0.23.35", "rustls-pemfile 2.2.0", "serde", "serde_json", "sha2", - "thiserror", + "thiserror 2.0.17", "tokio", ] @@ -1169,12 +2830,12 @@ dependencies = [ "futures-util", "log", "plist", - "rand", - "reqwest", + "rand 0.9.2", + "reqwest 0.11.27", "serde", "serde_json", "sha2", - "thiserror", + "thiserror 2.0.17", "tokio-tungstenite", "uuid", ] @@ -1205,7 +2866,7 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "tempfile", ] @@ -1230,6 +2891,22 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +dependencies = [ + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -1245,6 +2922,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1252,6 +2940,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "crc32fast", + "flate2", + "hashbrown 0.15.5", + "indexmap", + "memchr", + "ruzstd", +] + +[[package]] +name = "oid-registry" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" +dependencies = [ + "asn1-rs", ] [[package]] @@ -1260,6 +2972,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "opaque-debug" version = "0.3.1" @@ -1298,15 +3016,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" -[[package]] -name = "openssl-src" -version = "300.5.4+3.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507b3792995dae9b0df8a1c1e3771e8418b7c2d9f0baeba32e6fe8b06c7cb72" -dependencies = [ - "cc", -] - [[package]] name = "openssl-sys" version = "0.9.111" @@ -1315,11 +3024,61 @@ checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", - "openssl-src", "pkg-config", "vcpkg", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + +[[package]] +name = "p12" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4873306de53fe82e7e484df31e1e947d61514b6ea2ed6cd7b45d63006fd9224" +dependencies = [ + "cbc", + "cipher", + "des", + "getrandom 0.2.16", + "hmac", + "lazy_static", + "rc2", + "sha1", + "yasna", +] + +[[package]] +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +dependencies = [ + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", + "primeorder", +] + [[package]] name = "parking" version = "2.2.1" @@ -1349,6 +3108,48 @@ dependencies = [ "sha2", ] +[[package]] +name = "pear" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" +dependencies = [ + "inlinable_string", + "pear_codegen", + "yansi", +] + +[[package]] +name = "pear_codegen" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn", +] + +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64 0.22.1", + "serde_core", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -1387,23 +3188,60 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der 0.7.10", + "pkcs8 0.10.2", + "spki 0.7.3", +] + [[package]] name = "pkcs7" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d79178be066405e0602bf3035946edef6b11b3f9dde46dfe5f8bfd7dea4b77e7" dependencies = [ - "der", - "spki", + "der 0.7.10", + "spki 0.7.3", "x509-cert", ] +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der 0.6.1", + "spki 0.6.0", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.10", + "spki 0.7.3", +] + [[package]] name = "pkg-config" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + [[package]] name = "plist" version = "1.8.0" @@ -1429,6 +3267,21 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -1463,6 +3316,15 @@ dependencies = [ "syn", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve 0.13.8", +] + [[package]] name = "proc-macro2" version = "1.0.103" @@ -1472,6 +3334,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "version_check", + "yansi", +] + [[package]] name = "quick-xml" version = "0.38.3" @@ -1481,6 +3356,61 @@ dependencies = [ "memchr", ] +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.23.35", + "socket2 0.6.1", + "thiserror 2.0.17", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls 0.23.35", + "rustls-pki-types", + "slab", + "thiserror 2.0.17", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.6.1", + "tracing", + "windows-sys 0.60.2", +] + [[package]] name = "quote" version = "1.0.42" @@ -1496,16 +3426,43 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + [[package]] name = "rand" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ - "rand_chacha", + "rand_chacha 0.9.0", "rand_core 0.9.3", ] +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_chacha" version = "0.9.0" @@ -1534,6 +3491,107 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rasn" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e442690f86da40561d5548e7ffb4a18af90d1c1b3536090de847ca2d5a3a6426" +dependencies = [ + "arrayvec", + "bitvec", + "bitvec-nom2", + "bytes", + "chrono", + "either", + "hashbrown 0.14.5", + "konst", + "nom", + "num-bigint", + "num-integer", + "num-traits", + "once_cell", + "rasn-derive", + "serde_json", + "snafu", +] + +[[package]] +name = "rasn-derive" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0d374c7e4e985e6bc97ca7e7ad1d9642a8415db2017777d6e383002edaab2" +dependencies = [ + "either", + "itertools", + "proc-macro2", + "quote", + "rayon", + "syn", + "uuid", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rc2" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62c64daa8e9438b84aaae55010a93f396f8e60e3911590fcba770d04643fc1dd" +dependencies = [ + "cipher", +] + +[[package]] +name = "rcgen" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" +dependencies = [ + "pem", + "ring", + "rustls-pki-types", + "time", + "yasna", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 1.0.69", +] + [[package]] name = "regex" version = "1.12.2" @@ -1557,6 +3615,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-lite" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d942b98df5e658f56f20d592c7f868833fe38115e65c33003d8cd224b0155da" + [[package]] name = "regex-syntax" version = "0.8.8" @@ -1575,11 +3639,11 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2", + "h2 0.3.27", "http 0.2.12", - "http-body", - "hyper", - "hyper-rustls", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-rustls 0.24.2", "hyper-tls", "ipnet", "js-sys", @@ -1594,7 +3658,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", @@ -1609,6 +3673,59 @@ dependencies = [ "winreg", ] +[[package]] +name = "reqwest" +version = "0.12.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.4.12", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "hyper 1.8.1", + "hyper-rustls 0.27.7", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls 0.23.35", + "rustls-native-certs 0.8.2", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "tokio", + "tokio-rustls 0.26.4", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots 1.0.4", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint 0.4.9", + "hmac", + "zeroize", +] + [[package]] name = "ring" version = "0.17.14" @@ -1623,12 +3740,50 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rsa" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "signature 2.2.0", + "spki 0.7.3", + "subtle", + "zeroize", +] + [[package]] name = "rustc-hash" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + [[package]] name = "rustix" version = "1.1.2" @@ -1660,6 +3815,7 @@ version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ + "aws-lc-rs", "once_cell", "ring", "rustls-pki-types", @@ -1668,6 +3824,43 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile 1.0.4", + "schannel", + "security-framework 2.11.1", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.2.0", + "rustls-pki-types", + "schannel", + "security-framework 2.11.1", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework 3.5.1", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -1692,6 +3885,7 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" dependencies = [ + "web-time", "zeroize", ] @@ -1711,6 +3905,7 @@ version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -1722,12 +3917,30 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "ruzstd" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad02996bfc73da3e301efe90b1837be9ed8f4a462b6ed410aa35d00381de89f" +dependencies = [ + "twox-hash", +] + [[package]] name = "ryu" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.28" @@ -1737,6 +3950,26 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "scroll" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "sct" version = "0.7.1" @@ -1747,6 +3980,34 @@ dependencies = [ "untrusted", ] +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct 0.1.1", + "der 0.6.1", + "generic-array 0.14.9", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.10", + "generic-array 0.14.9", + "pkcs8 0.10.2", + "subtle", + "zeroize", +] + [[package]] name = "security-framework" version = "2.11.1" @@ -1754,7 +4015,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags 2.10.0", - "core-foundation", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +dependencies = [ + "bitflags 2.10.0", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -1770,6 +4044,12 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + [[package]] name = "serde" version = "1.0.228" @@ -1780,6 +4060,18 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-xml-rs" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb3aa78ecda1ebc9ec9847d5d3aba7d618823446a049ba2491940506da6e2782" +dependencies = [ + "log", + "serde", + "thiserror 1.0.69", + "xml-rs", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -1813,6 +4105,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1825,6 +4126,19 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "sha1" version = "0.10.6" @@ -1847,18 +4161,71 @@ dependencies = [ "digest", ] +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + [[package]] name = "simd-adler32" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simple-file-manifest" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd19be0257552dd56d1bb6946f89f193c6e5b9f13cc9327c4bc84a357507c74" + +[[package]] +name = "simple_asn1" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror 2.0.17", + "time", +] + [[package]] name = "slab" version = "0.4.11" @@ -1871,6 +4238,27 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "snafu" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e84b3f4eacbf3a1ce05eac6763b4d629d60cbc94d632e4092c54ade71f1e1a2" +dependencies = [ + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1c97747dbf44bb1ca44a561ece23508e99cb592e862f22222dcf42f51d1e451" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "socket2" version = "0.5.10" @@ -1891,6 +4279,34 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "spake2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5482afe85a0b6ce956c945401598dbc527593c77ba51d0a87a586938b1b893a" +dependencies = [ + "curve25519-dalek", + "hkdf", + "rand_core 0.6.4", + "sha2", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der 0.6.1", +] + [[package]] name = "spki" version = "0.7.3" @@ -1898,7 +4314,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der", + "der 0.7.10", ] [[package]] @@ -1907,6 +4323,18 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "subtle" version = "2.6.1" @@ -1930,6 +4358,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + [[package]] name = "synstructure" version = "0.13.2" @@ -1948,7 +4385,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -1962,6 +4399,12 @@ dependencies = [ "libc", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.23.0" @@ -1975,13 +4418,33 @@ dependencies = [ "windows-sys 0.61.2", ] +[[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.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.17", +] + +[[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", ] [[package]] @@ -2036,6 +4499,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.48.0" @@ -2046,6 +4524,7 @@ dependencies = [ "libc", "mio", "pin-project-lite", + "signal-hook-registry", "socket2 0.6.1", "tokio-macros", "windows-sys 0.61.2", @@ -2104,7 +4583,7 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls 0.26.4", - "tungstenite", + "tungstenite 0.27.0", "webpki-roots 0.26.11", ] @@ -2122,6 +4601,86 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 1.0.2", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags 2.10.0", + "bytes", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" version = "0.3.3" @@ -2165,6 +4724,27 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.3.1", + "httparse", + "log", + "rand 0.8.5", + "rustls 0.23.35", + "rustls-native-certs 0.7.3", + "rustls-pki-types", + "sha1", + "thiserror 1.0.69", + "utf-8", +] + [[package]] name = "tungstenite" version = "0.27.0" @@ -2176,26 +4756,66 @@ dependencies = [ "http 1.3.1", "httparse", "log", - "rand", + "rand 0.9.2", "rustls 0.23.35", "rustls-pki-types", "sha1", - "thiserror", + "thiserror 2.0.17", "utf-8", ] +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "static_assertions", +] + [[package]] name = "typenum" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "typewit" +version = "1.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c1ae7cc0fdb8b842d65d127cb981574b0d2b249b74d1c7a2986863dc134f71" +dependencies = [ + "typewit_proc_macros", +] + +[[package]] +name = "typewit_proc_macros" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6" + +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-ident" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + [[package]] name = "universal-hash" version = "0.5.1" @@ -2206,6 +4826,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "untrusted" version = "0.9.0" @@ -2224,6 +4850,12 @@ dependencies = [ "serde", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf-8" version = "0.7.6" @@ -2236,6 +4868,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.18.1" @@ -2244,7 +4882,7 @@ checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ "getrandom 0.3.4", "js-sys", - "rand", + "rand 0.9.2", "uuid-macro-internal", "wasm-bindgen", ] @@ -2272,6 +4910,22 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -2364,6 +5018,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" version = "0.25.4" @@ -2388,6 +5052,21 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "widestring" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "windows-core" version = "0.62.2" @@ -2465,6 +5144,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.60.2" @@ -2669,6 +5357,15 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" @@ -2691,6 +5388,25 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x509" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3cec94c3999f31341553f358ef55f65fc031291a022cd42ec0ce7219560c76" +dependencies = [ + "chrono", + "cookie-factory", +] + [[package]] name = "x509-cert" version = "0.2.5" @@ -2698,8 +5414,82 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" dependencies = [ "const-oid", - "der", - "spki", + "der 0.7.10", + "spki 0.7.3", +] + +[[package]] +name = "x509-certificate" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57b9f8bcae7c1f36479821ae826d75050c60ce55146fd86d3553ed2573e2762" +dependencies = [ + "bcder", + "bytes", + "chrono", + "der 0.7.10", + "hex", + "pem", + "ring", + "signature 2.2.0", + "spki 0.7.3", + "thiserror 1.0.69", + "zeroize", +] + +[[package]] +name = "x509-certificate" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca9eb9a0c822c67129d5b8fcc2806c6bc4f50496b420825069a440669bcfbf7f" +dependencies = [ + "bcder", + "bytes", + "chrono", + "der 0.7.10", + "hex", + "pem", + "ring", + "signature 2.2.0", + "spki 0.7.3", + "thiserror 2.0.17", + "zeroize", +] + +[[package]] +name = "xml-rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" + +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + +[[package]] +name = "xz2" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +dependencies = [ + "lzma-sys", +] + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", ] [[package]] @@ -2771,6 +5561,20 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "zerotrie" @@ -2805,6 +5609,23 @@ dependencies = [ "syn", ] +[[package]] +name = "zip" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" +dependencies = [ + "arbitrary", + "crc32fast", + "crossbeam-utils", + "displaydoc", + "flate2", + "indexmap", + "memchr", + "thiserror 2.0.17", + "zopfli", +] + [[package]] name = "zip" version = "4.6.1" @@ -2819,6 +5640,17 @@ dependencies = [ "zopfli", ] +[[package]] +name = "zip_structs" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce824a6bfffe8942820fa36d24973b7c83a40896749a42e33de0abdd11750ee5" +dependencies = [ + "byteorder", + "bytesize", + "thiserror 1.0.69", +] + [[package]] name = "zlib-rs" version = "0.5.2" @@ -2836,17 +5668,3 @@ dependencies = [ "log", "simd-adler32", ] - -[[package]] -name = "zsign-rust" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e6f7303e79779f24807ebb43de1816334c6bbbfb1982785d21189bc1cb47f79" -dependencies = [ - "bindgen", - "cc", - "openssl-src", - "pkg-config", - "thiserror", - "vcpkg", -] diff --git a/README.md b/README.md index 0bbb6eb..048c601 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,6 @@ This project is licensed under the MPL-2.0 License. See the [LICENSE](LICENSE) f - Packages from [`apple-private-apis`](https://github.com/SideStore/apple-private-apis) were used for authentication, but the original project was left unfinished. To support isideload, `apple-private-apis` was forked and modified to add missing features. With permission from the original developers, the fork was published to crates.io until the official project is published. -- [ZSign](https://github.com/zhlynn/zsign) was used for code signing with [custom rust bindings](https://github.com/nab138/zsign-rust) +- [apple-codesign](https://crates.io/crates/apple-codesign) was used for code signing, which is licensed under MPL-2.0. - [Sideloader](https://github.com/Dadoum/Sideloader) was used as a reference for how the private API endpoints work diff --git a/examples/minimal/Cargo.toml b/examples/minimal/Cargo.toml index 14aae5b..33e8b55 100644 --- a/examples/minimal/Cargo.toml +++ b/examples/minimal/Cargo.toml @@ -5,6 +5,6 @@ edition = "2024" publish = false [dependencies] -isideload = { path = "../../isideload", features = ["vendored-openssl"] } +isideload = { path = "../../isideload" } idevice = { version = "0.1.46", features = ["usbmuxd", "ring"], default-features = false} tokio = { version = "1.43", features = ["macros", "rt-multi-thread"] } diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 20b2d38..b9581d3 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -12,7 +12,6 @@ readme = "../README.md" [features] default = [] -vendored-openssl = ["openssl/vendored", "zsign-rust/vendored-openssl"] [dependencies] serde = { version = "1", features = ["derive"] } @@ -23,6 +22,12 @@ zip = { version = "4.3", default-features = false, features = ["deflate"] } hex = "0.4" sha1 = "0.10" idevice = { version = "0.1.46", features = ["afc", "installation_proxy", "ring"], default-features = false } -openssl = "0.10" -zsign-rust = "0.1.6" thiserror = "2" +apple-codesign = "0.29.0" +x509-certificate = "0.25.0" +rsa = "0.9" +pkcs8 = "0.10" +rcgen = "0.13" +p12 = "0.6" +der = "0.7" +rand = "0.8" diff --git a/isideload/src/certificate.rs b/isideload/src/certificate.rs index 59f8f44..b1dd552 100644 --- a/isideload/src/certificate.rs +++ b/isideload/src/certificate.rs @@ -1,26 +1,27 @@ // This file was made using https://github.com/Dadoum/Sideloader as a reference. use hex; -use openssl::{ - hash::MessageDigest, - pkcs12::Pkcs12, - pkey::{PKey, Private}, - rsa::Rsa, - x509::{X509, X509Name, X509ReqBuilder}, +use rcgen::{CertificateParams, DnType, KeyPair}; +use rsa::pkcs1::EncodeRsaPublicKey; +use rsa::{ + RsaPrivateKey, + pkcs8::{DecodePrivateKey, EncodePrivateKey, LineEnding}, }; use sha1::{Digest, Sha1}; use std::{ fs, path::{Path, PathBuf}, + process::Command, }; +use x509_certificate::X509Certificate; use crate::Error; use crate::developer_session::{DeveloperDeviceType, DeveloperSession, DeveloperTeam}; #[derive(Debug, Clone)] pub struct CertificateIdentity { - pub certificate: Option, - pub private_key: PKey, + pub certificate: Option, + pub private_key: RsaPrivateKey, pub key_file: PathBuf, pub cert_file: PathBuf, pub machine_name: String, @@ -46,21 +47,22 @@ impl CertificateIdentity { let team = teams .first() .ok_or(Error::Certificate("No teams found".to_string()))?; + let private_key = if key_file.exists() { let key_data = fs::read_to_string(&key_file) .map_err(|e| Error::Certificate(format!("Failed to read key file: {}", e)))?; - PKey::private_key_from_pem(key_data.as_bytes()) + RsaPrivateKey::from_pkcs8_pem(&key_data) .map_err(|e| Error::Certificate(format!("Failed to load private key: {}", e)))? } else { - let rsa = Rsa::generate(2048) + let mut rng = rand::thread_rng(); + let private_key = RsaPrivateKey::new(&mut rng, 2048) .map_err(|e| Error::Certificate(format!("Failed to generate RSA key: {}", e)))?; - let key = PKey::from_rsa(rsa) - .map_err(|e| Error::Certificate(format!("Failed to create private key: {}", e)))?; - let pem_data = key - .private_key_to_pem_pkcs8() + + let pem_data = private_key + .to_pkcs8_pem(LineEnding::LF) .map_err(|e| Error::Certificate(format!("Failed to encode private key: {}", e)))?; - fs::write(&key_file, pem_data).map_err(Error::Filesystem)?; - key + fs::write(&key_file, pem_data.as_bytes()).map_err(Error::Filesystem)?; + private_key }; let mut cert_identity = CertificateIdentity { @@ -78,9 +80,10 @@ impl CertificateIdentity { { cert_identity.certificate = Some(cert.clone()); cert_identity.machine_id = machine_id; - let cert_pem = cert.to_pem().map_err(|e| { - Error::Certificate(format!("Failed to encode certificate to PEM: {}", e)) - })?; + + let cert_pem = cert + .encode_pem() + .map_err(|e| Error::Certificate(format!("Failed to encode cert: {}", e)))?; fs::write(&cert_identity.cert_file, cert_pem).map_err(Error::Filesystem)?; return Ok(cert_identity); @@ -96,27 +99,33 @@ impl CertificateIdentity { &self, dev_session: &DeveloperSession, team: &DeveloperTeam, - ) -> Result<(X509, String), Error> { + ) -> Result<(X509Certificate, String), Error> { let certificates = dev_session .list_all_development_certs(DeveloperDeviceType::Ios, team) .await .map_err(|e| Error::Certificate(format!("Failed to list certificates: {:?}", e)))?; - let our_public_key = self + let our_public_key_der = self .private_key - .public_key_to_der() - .map_err(|e| Error::Certificate(format!("Failed to get public key: {}", e)))?; + .to_public_key() + .to_pkcs1_der() + .map_err(|e| Error::Certificate(format!("Failed to get public key: {}", e)))? + .to_vec(); for cert in certificates .iter() .filter(|c| c.machine_name == self.machine_name) { - if let Ok(x509_cert) = X509::from_der(&cert.cert_content) - && let Ok(cert_public_key) = x509_cert.public_key() - && let Ok(cert_public_key_der) = cert_public_key.public_key_to_der() - && cert_public_key_der == our_public_key - { - return Ok((x509_cert, cert.machine_id.clone())); + if let Ok(x509_cert) = X509Certificate::from_der(&cert.cert_content) { + let cert_public_key_der: Vec = x509_cert + .tbs_certificate() + .subject_public_key_info + .subject_public_key + .octets() + .collect(); + if cert_public_key_der == our_public_key_der { + return Ok((x509_cert, cert.machine_id.clone())); + } } } Err(Error::Certificate( @@ -129,47 +138,39 @@ impl CertificateIdentity { dev_session: &DeveloperSession, team: &DeveloperTeam, ) -> Result<(), Error> { - let mut req_builder = X509ReqBuilder::new() - .map_err(|e| Error::Certificate(format!("Failed to create request builder: {}", e)))?; - let mut name_builder = X509Name::builder() - .map_err(|e| Error::Certificate(format!("Failed to create name builder: {}", e)))?; + let mut params = CertificateParams::new(vec!["CN".to_string()]) + .map_err(|e| Error::Certificate(format!("Failed to create params: {}", e)))?; + params.distinguished_name.push(DnType::CountryName, "US"); + params + .distinguished_name + .push(DnType::StateOrProvinceName, "STATE"); + params + .distinguished_name + .push(DnType::LocalityName, "LOCAL"); + params + .distinguished_name + .push(DnType::OrganizationName, "ORGNIZATION"); + params.distinguished_name.push(DnType::CommonName, "CN"); - name_builder - .append_entry_by_text("C", "US") - .map_err(|e| Error::Certificate(format!("Failed to set country: {}", e)))?; - name_builder - .append_entry_by_text("ST", "STATE") - .map_err(|e| Error::Certificate(format!("Failed to set state: {}", e)))?; - name_builder - .append_entry_by_text("L", "LOCAL") - .map_err(|e| Error::Certificate(format!("Failed to set locality: {}", e)))?; - name_builder - .append_entry_by_text("O", "ORGNIZATION") - .map_err(|e| Error::Certificate(format!("Failed to set organization: {}", e)))?; - name_builder - .append_entry_by_text("CN", "CN") - .map_err(|e| Error::Certificate(format!("Failed to set common name: {}", e)))?; + let key_pem = self + .private_key + .to_pkcs8_pem(LineEnding::LF) + .map_err(|e| Error::Certificate(format!("Failed to encode private key: {}", e)))?; + let key_pair = KeyPair::from_pem(&key_pem) + .map_err(|e| Error::Certificate(format!("Failed to load key pair for CSR: {}", e)))?; - req_builder - .set_subject_name(&name_builder.build()) - .map_err(|e| Error::Certificate(format!("Failed to set subject name: {}", e)))?; - req_builder - .set_pubkey(&self.private_key) - .map_err(|e| Error::Certificate(format!("Failed to set public key: {}", e)))?; - req_builder - .sign(&self.private_key, MessageDigest::sha256()) - .map_err(|e| Error::Certificate(format!("Failed to sign request: {}", e)))?; - - let csr_pem = req_builder - .build() - .to_pem() - .map_err(|e| Error::Certificate(format!("Failed to encode CSR: {}", e)))?; + let csr = params + .serialize_request(&key_pair) + .map_err(|e| Error::Certificate(format!("Failed to generate CSR: {}", e)))?; + let csr_pem = csr + .pem() + .map_err(|e| Error::Certificate(format!("Failed to encode CSR to PEM: {}", e)))?; let certificate_id = dev_session .submit_development_csr( DeveloperDeviceType::Ios, team, - String::from_utf8_lossy(&csr_pem).to_string(), + csr_pem, self.machine_name.clone(), ) .await @@ -196,13 +197,13 @@ impl CertificateIdentity { "Certificate not found after submission".to_string(), ))?; - let certificate = X509::from_der(&apple_cert.cert_content) + let certificate = X509Certificate::from_der(&apple_cert.cert_content) .map_err(|e| Error::Certificate(format!("Failed to parse certificate: {}", e)))?; // Write certificate to disk - let cert_pem = certificate.to_pem().map_err(|e| { - Error::Certificate(format!("Failed to encode certificate to PEM: {}", e)) - })?; + let cert_pem = certificate + .encode_pem() + .map_err(|e| Error::Certificate(format!("Failed to encode cert: {}", e)))?; fs::write(&self.cert_file, cert_pem).map_err(Error::Filesystem)?; self.certificate = Some(certificate); @@ -228,46 +229,33 @@ impl CertificateIdentity { )); } }; - let num = cert - .serial_number() - .to_bn() - .map_err(|e| { - Error::Certificate(format!("Failed to convert serial number to bn: {}", e)) - })? - .to_hex_str() - .map_err(|e| { - Error::Certificate(format!( - "Failed to convert serial number to hex string: {}", - e - )) - })? - .to_string(); - Ok(num.trim_start_matches("0").to_string()) + let serial = &cert.tbs_certificate().serial_number; + let hex_str = hex::encode(serial.as_slice()); + + Ok(hex_str.trim_start_matches("0").to_string()) } pub fn to_pkcs12(&self, password: &str) -> Result, Error> { - let cert = match &self.certificate { - Some(c) => c, - None => { - return Err(Error::Certificate( - "No certificate available to create PKCS#12".to_string(), - )); - } - }; + let output = Command::new("openssl") + .arg("pkcs12") + .arg("-export") + .arg("-inkey") + .arg(&self.key_file) + .arg("-in") + .arg(&self.cert_file) + .arg("-passout") + .arg(format!("pass:{}", password)) + .output() + .map_err(|e| Error::Certificate(format!("Failed to execute openssl: {}", e)))?; - let mut pkcs12_builder = Pkcs12::builder(); - pkcs12_builder.pkey(&self.private_key); - pkcs12_builder.cert(cert); - pkcs12_builder.name("certificate"); - let pkcs12 = pkcs12_builder - .build2(password) - .map_err(|e| Error::Certificate(format!("Failed to create PKCS#12 bundle: {}", e)))?; + if !output.status.success() { + return Err(Error::Certificate(format!( + "openssl failed: {}", + String::from_utf8_lossy(&output.stderr) + ))); + } - let der_bytes = pkcs12 - .to_der() - .map_err(|e| Error::Certificate(format!("Failed to encode PKCS#12 to DER: {}", e)))?; - - Ok(der_bytes) + Ok(output.stdout) } } diff --git a/isideload/src/lib.rs b/isideload/src/lib.rs index 698bd75..108df09 100644 --- a/isideload/src/lib.rs +++ b/isideload/src/lib.rs @@ -7,12 +7,12 @@ pub mod sideload; use std::io::Error as IOError; +use apple_codesign::AppleCodesignError; pub use icloud_auth::{AnisetteConfiguration, AppleAccount}; use developer_session::DeveloperTeam; use idevice::IdeviceError; use thiserror::Error as ThisError; -use zsign_rust::ZSignError; #[derive(Debug, ThisError)] pub enum Error { @@ -33,7 +33,7 @@ pub enum Error { #[error(transparent)] IdeviceError(#[from] IdeviceError), #[error(transparent)] - ZSignError(#[from] ZSignError), + AppleCodesignError(#[from] Box), } pub trait SideloadLogger: Send + Sync { diff --git a/isideload/src/sideload.rs b/isideload/src/sideload.rs index 39b51dc..2a344e4 100644 --- a/isideload/src/sideload.rs +++ b/isideload/src/sideload.rs @@ -1,9 +1,10 @@ // This file was made using https://github.com/Dadoum/Sideloader as a reference. +use apple_codesign::{BundleSigner, SigningSettings}; +use der::Encode; use idevice::IdeviceService; use idevice::lockdown::LockdownClient; use idevice::provider::IdeviceProvider; -use zsign_rust::ZSignOptions; use crate::application::Application; use crate::device::install_app; @@ -385,18 +386,32 @@ pub async fn sideload_app( ext.write_info()?; } - 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, Error::ZSignError(e)); - } - }; + // 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, Error::ZSignError(e)); + // } + // }; + let mut signer = BundleSigner::new_from_path(&app.bundle.bundle_dir) + .map_err(|e| Error::AppleCodesignError(Box::new(e)))?; + + signer + .collect_nested_bundles() + .map_err(|e| Error::AppleCodesignError(Box::new(e)))?; + + let mut settings = SigningSettings::default(); + + settings.set_signing_key(cert.private_key.clone(), cert.certificate.unwrap()); + + signer + .write_signed_bundle(&app.bundle.bundle_dir, &settings) + .map_err(|e| Error::AppleCodesignError(Box::new(e)))?; logger.log("App signed!"); logger.log("Installing app (Transfer)... 0%"); From cdedc84e8b8e3bc9d608415c78393c216374dfe7 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 23 Nov 2025 11:03:52 -0500 Subject: [PATCH 02/71] im questioning everything I thought I knew about this stuff --- Cargo.lock | 32 ++------ examples/minimal/src/main.rs | 4 +- isideload/Cargo.toml | 7 +- isideload/src/bundle.rs | 42 +++++++++- isideload/src/certificate.rs | 98 +++++++++++++++------- isideload/src/developer_session.rs | 53 ++++++++++++ isideload/src/lib.rs | 30 ++++--- isideload/src/sideload.rs | 126 +++++++++++++++-------------- 8 files changed, 258 insertions(+), 134 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4bacf27..cb09758 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -155,7 +155,7 @@ dependencies = [ "serde", "serde_json", "thiserror 2.0.17", - "x509-certificate 0.24.0", + "x509-certificate", ] [[package]] @@ -246,7 +246,7 @@ dependencies = [ "widestring", "windows-sys 0.59.0", "x509", - "x509-certificate 0.24.0", + "x509-certificate", "xml-rs", "yasna", "zeroize", @@ -294,7 +294,7 @@ dependencies = [ "signature 2.2.0", "thiserror 2.0.17", "url", - "x509-certificate 0.24.0", + "x509-certificate", "xml-rs", "xz2", ] @@ -1390,7 +1390,7 @@ dependencies = [ "reqwest 0.12.24", "ring", "signature 2.2.0", - "x509-certificate 0.24.0", + "x509-certificate", ] [[package]] @@ -2504,12 +2504,10 @@ name = "isideload" version = "0.1.21" dependencies = [ "apple-codesign", - "der 0.7.10", "hex", "idevice", "nab138_icloud_auth", "p12", - "pkcs8 0.10.2", "plist", "rand 0.8.5", "rcgen", @@ -2517,8 +2515,9 @@ dependencies = [ "serde", "sha1", "thiserror 2.0.17", + "tokio", "uuid", - "x509-certificate 0.25.0", + "x509-certificate", "zip 4.6.1", ] @@ -5437,25 +5436,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "x509-certificate" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca9eb9a0c822c67129d5b8fcc2806c6bc4f50496b420825069a440669bcfbf7f" -dependencies = [ - "bcder", - "bytes", - "chrono", - "der 0.7.10", - "hex", - "pem", - "ring", - "signature 2.2.0", - "spki 0.7.3", - "thiserror 2.0.17", - "zeroize", -] - [[package]] name = "xml-rs" version = "0.8.28" diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index aa49688..9b2e9a9 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -58,7 +58,9 @@ async fn main() { let dev_session = DeveloperSession::new(Arc::new(account)); // You can change the machine name, store directory (for certs, anisette data, & provision files), and logger - let config = SideloadConfiguration::default().set_machine_name("isideload-demo".to_string()); + let config = SideloadConfiguration::default() + .set_machine_name("isideload-demo".to_string()) + .set_force_sidestore(true); sideload_app(&provider, &dev_session, app_path, config) .await diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index b9581d3..1675b36 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -24,10 +24,9 @@ sha1 = "0.10" idevice = { version = "0.1.46", features = ["afc", "installation_proxy", "ring"], default-features = false } thiserror = "2" apple-codesign = "0.29.0" -x509-certificate = "0.25.0" +x509-certificate = "0.24.0" rsa = "0.9" -pkcs8 = "0.10" rcgen = "0.13" -p12 = "0.6" -der = "0.7" rand = "0.8" +tokio = "1.48.0" +p12 = "0.6.3" diff --git a/isideload/src/bundle.rs b/isideload/src/bundle.rs index 117acfd..c4fc36c 100644 --- a/isideload/src/bundle.rs +++ b/isideload/src/bundle.rs @@ -7,12 +7,13 @@ use std::{ path::{Path, PathBuf}, }; +#[derive(Debug, Clone)] pub struct Bundle { pub app_info: Dictionary, pub bundle_dir: PathBuf, - + pub bundle_type: BundleType, app_extensions: Vec, - _frameworks: Vec, + frameworks: Vec, _libraries: Vec, } @@ -79,9 +80,15 @@ impl Bundle { Ok(Bundle { app_info, + bundle_type: BundleType::from_extension( + bundle_path + .extension() + .and_then(|ext| ext.to_str()) + .unwrap_or(""), + ), bundle_dir: bundle_path, app_extensions, - _frameworks: frameworks, + frameworks, _libraries: libraries, }) } @@ -125,6 +132,15 @@ impl Bundle { } Ok(()) } + + pub fn embedded_bundles(&self) -> Vec<&Bundle> { + let mut bundles = Vec::new(); + bundles.extend(self.app_extensions.iter()); + bundles.extend(self.frameworks.iter()); + bundles.push(self); + bundles.sort_by_key(|b| b.bundle_dir.components().count()); + bundles + } } fn assert_bundle(condition: bool, msg: &str) -> Result<(), Error> { @@ -178,3 +194,23 @@ fn find_dylibs(dir: &Path, bundle_root: &Path) -> Result, Error> { collect_dylibs(dir, bundle_root, &mut libraries)?; Ok(libraries) } + +// Borrowed from https://github.com/khcrysalis/PlumeImpactor/blob/main/crates/utils/src/bundle.rs +#[derive(Debug, Clone)] +pub enum BundleType { + App, + AppExtension, + Framework, + Unknown, +} + +impl BundleType { + pub fn from_extension(ext: &str) -> Self { + match ext { + "app" => BundleType::App, + "appex" => BundleType::AppExtension, + "framework" => BundleType::Framework, + _ => BundleType::Unknown, + } + } +} diff --git a/isideload/src/certificate.rs b/isideload/src/certificate.rs index b1dd552..feb445b 100644 --- a/isideload/src/certificate.rs +++ b/isideload/src/certificate.rs @@ -1,8 +1,8 @@ // This file was made using https://github.com/Dadoum/Sideloader as a reference. +use apple_codesign::SigningSettings; use hex; use rcgen::{CertificateParams, DnType, KeyPair}; -use rsa::pkcs1::EncodeRsaPublicKey; use rsa::{ RsaPrivateKey, pkcs8::{DecodePrivateKey, EncodePrivateKey, LineEnding}, @@ -11,16 +11,16 @@ use sha1::{Digest, Sha1}; use std::{ fs, path::{Path, PathBuf}, - process::Command, }; -use x509_certificate::X509Certificate; +use x509_certificate::{CapturedX509Certificate, InMemorySigningKeyPair, Sign, X509Certificate}; use crate::Error; use crate::developer_session::{DeveloperDeviceType, DeveloperSession, DeveloperTeam}; -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct CertificateIdentity { pub certificate: Option, + pub key_pair: InMemorySigningKeyPair, pub private_key: RsaPrivateKey, pub key_file: PathBuf, pub cert_file: PathBuf, @@ -65,8 +65,17 @@ impl CertificateIdentity { private_key }; + let key_pair = InMemorySigningKeyPair::from_pkcs8_der( + private_key + .to_pkcs8_der() + .map_err(|e| Error::Certificate(format!("Failed to encode private key: {}", e)))? + .as_bytes(), + ) + .map_err(|e| Error::Certificate(format!("Failed to decode private key: {}", e)))?; + let mut cert_identity = CertificateIdentity { certificate: None, + key_pair, private_key, key_file, cert_file, @@ -105,12 +114,7 @@ impl CertificateIdentity { .await .map_err(|e| Error::Certificate(format!("Failed to list certificates: {:?}", e)))?; - let our_public_key_der = self - .private_key - .to_public_key() - .to_pkcs1_der() - .map_err(|e| Error::Certificate(format!("Failed to get public key: {}", e)))? - .to_vec(); + let our_public_key_der = self.key_pair.public_key_data().to_vec(); for cert in certificates .iter() @@ -233,29 +237,65 @@ impl CertificateIdentity { let serial = &cert.tbs_certificate().serial_number; let hex_str = hex::encode(serial.as_slice()); - Ok(hex_str.trim_start_matches("0").to_string()) + Ok(hex_str.trim_start_matches("0").to_string().to_uppercase()) } pub fn to_pkcs12(&self, password: &str) -> Result, Error> { - let output = Command::new("openssl") - .arg("pkcs12") - .arg("-export") - .arg("-inkey") - .arg(&self.key_file) - .arg("-in") - .arg(&self.cert_file) - .arg("-passout") - .arg(format!("pass:{}", password)) - .output() - .map_err(|e| Error::Certificate(format!("Failed to execute openssl: {}", e)))?; + let cert = self + .certificate + .as_ref() + .ok_or(Error::Certificate("Certificate not found".to_string()))?; - if !output.status.success() { - return Err(Error::Certificate(format!( - "openssl failed: {}", - String::from_utf8_lossy(&output.stderr) - ))); - } + let cert_der = cert + .encode_der() + .map_err(|e| Error::Certificate(format!("Failed to encode certificate: {}", e)))?; - Ok(output.stdout) + let key_der = self + .private_key + .to_pkcs8_der() + .map_err(|e| Error::Certificate(format!("Failed to encode private key: {}", e)))?; + + let pfx = p12::PFX::new( + &cert_der, + key_der.as_bytes(), + None, + password, + &self.machine_name, + ) + .ok_or(Error::Certificate("Failed to create PKCS#12".to_string()))?; + + Ok(pfx.to_der()) + } + + pub fn to_signing_settings(&self) -> Result, Error> { + let mut settings = SigningSettings::default(); + + let certificate = self + .certificate + .as_ref() + .ok_or(Error::Certificate("Certificate not found".to_string()))?; + + settings.set_signing_key( + &self.key_pair, + CapturedX509Certificate::from_der( + certificate.encode_der().map_err(|e| { + Error::Certificate(format!("Failed to encode certificate: {}", e)) + })?, + ) + .map_err(|e| { + Error::Certificate(format!("Failed to create captured certificate: {}", e)) + })?, + ); + settings.chain_apple_certificates(); + settings.set_for_notarization(false); + settings.set_shallow(true); + settings.set_team_id_from_signing_certificate().ok_or({ + Error::Certificate("Failed to set team ID from signing certificate".to_string()) + })?; + settings + .set_time_stamp_url("http://timestamp.apple.com/ts01") + .map_err(|e| Error::AppleCodesignError(Box::new(e)))?; + + Ok(settings) } } diff --git a/isideload/src/developer_session.rs b/isideload/src/developer_session.rs index 732c885..3e70dd4 100644 --- a/isideload/src/developer_session.rs +++ b/isideload/src/developer_session.rs @@ -724,3 +724,56 @@ pub struct ProvisioningProfile { pub _name: String, pub encoded_profile: Vec, } + +impl ProvisioningProfile { + // TODO: I'm not sure if this is the proper way to parse this but it works so... + pub fn profile_plist(&self) -> Result { + let start_marker = b""; + + let start = self + .encoded_profile + .windows(start_marker.len()) + .position(|w| w == start_marker) + .ok_or_else(|| { + Error::Generic("Failed to find start of plist in provisioning profile".to_string()) + })?; + + let end = self + .encoded_profile + .windows(end_marker.len()) + .position(|w| w == end_marker) + .ok_or_else(|| { + Error::Generic("Failed to find end of plist in provisioning profile".to_string()) + })? + + end_marker.len(); + + plist::from_bytes::(&self.encoded_profile[start..end]).map_err(|e| { + Error::Generic(format!("Failed to parse provisioning profile plist: {}", e)) + }) + } + + pub fn entitlements_xml(&self) -> Result { + let profile_plist = self.profile_plist()?; + let entitlements = profile_plist.get("Entitlements").ok_or_else(|| { + Error::Generic("No Entitlements found in provisioning profile".to_string()) + })?; + let mut buf = vec![]; + entitlements.to_writer_xml(&mut buf).map_err(|e| { + Error::Generic(format!( + "Failed to convert entitlements to XML for codesigning: {}", + e + )) + })?; + let entitlements = std::str::from_utf8(&buf) + .map_err(|e| { + Error::Generic(format!( + "Failed to convert entitlements to UTF-8 for codesigning: {}", + e + )) + })? + .to_string(); + + Ok(entitlements) + } +} diff --git a/isideload/src/lib.rs b/isideload/src/lib.rs index 108df09..8b843f0 100644 --- a/isideload/src/lib.rs +++ b/isideload/src/lib.rs @@ -55,16 +55,12 @@ impl SideloadLogger for DefaultLogger { /// Sideload configuration options. pub struct SideloadConfiguration<'a> { - /// An arbitrary machine name to appear on the certificate (e.x. "YCode") pub machine_name: String, - /// Logger for reporting progress and errors pub logger: &'a dyn SideloadLogger, - /// Directory used to store intermediate artifacts (profiles, certs, etc.). This directory will not be cleared at the end. pub store_dir: std::path::PathBuf, - /// Whether or not to revoke the certificate immediately after installation pub revoke_cert: bool, - /// Whether or not to force SideStore App Group (fixes LiveContainer+SideStore issues) - pub force_sidestore_app_group: bool, + pub force_sidestore: bool, + pub skip_register_extensions: bool, } impl Default for SideloadConfiguration<'_> { @@ -80,32 +76,48 @@ impl<'a> SideloadConfiguration<'a> { logger: &DefaultLogger, store_dir: std::env::current_dir().unwrap(), revoke_cert: false, - force_sidestore_app_group: false, + force_sidestore: false, + skip_register_extensions: true, } } + /// An arbitrary machine name to appear on the certificate (e.x. "CrossCode") pub fn set_machine_name(mut self, machine_name: String) -> Self { self.machine_name = machine_name; self } + /// Logger for reporting progress and errors pub fn set_logger(mut self, logger: &'a dyn SideloadLogger) -> Self { self.logger = logger; self } + /// Directory used to store intermediate artifacts (profiles, certs, etc.). This directory will not be cleared at the end. pub fn set_store_dir(mut self, store_dir: std::path::PathBuf) -> Self { self.store_dir = store_dir; self } + /// Whether or not to revoke the certificate immediately after installation + #[deprecated( + since = "0.1.0", + note = "Certificates will now be placed in SideStore automatically so there is no need to revoke" + )] pub fn set_revoke_cert(mut self, revoke_cert: bool) -> Self { self.revoke_cert = revoke_cert; self } - pub fn set_force_sidestore_app_group(mut self, force: bool) -> Self { - self.force_sidestore_app_group = force; + /// Whether or not to treat the app as SideStore (fixes LiveContainer+SideStore issues) + pub fn set_force_sidestore(mut self, force: bool) -> Self { + self.force_sidestore = force; + self + } + + /// Whether or not to skip registering app extensions (save app IDs, default true) + pub fn set_skip_register_extensions(mut self, skip: bool) -> Self { + self.skip_register_extensions = skip; self } } diff --git a/isideload/src/sideload.rs b/isideload/src/sideload.rs index 2a344e4..c150208 100644 --- a/isideload/src/sideload.rs +++ b/isideload/src/sideload.rs @@ -1,18 +1,19 @@ // This file was made using https://github.com/Dadoum/Sideloader as a reference. -use apple_codesign::{BundleSigner, SigningSettings}; -use der::Encode; +use apple_codesign::{SettingsScope, UnifiedSigner}; use idevice::IdeviceService; use idevice::lockdown::LockdownClient; use idevice::provider::IdeviceProvider; use crate::application::Application; +use crate::developer_session::ProvisioningProfile; use crate::device::install_app; use crate::{DeveloperTeam, Error, SideloadConfiguration, SideloadLogger}; use crate::{ certificate::CertificateIdentity, developer_session::{DeveloperDeviceType, DeveloperSession}, }; +use std::collections::HashMap; use std::{io::Write, path::PathBuf}; fn error_and_return(logger: &dyn SideloadLogger, error: Error) -> Result<(), Error> { @@ -106,7 +107,8 @@ pub async fn sideload_app( }; let mut app = Application::new(app_path)?; - let is_sidestore = app.bundle.bundle_identifier().unwrap_or("") == "com.SideStore.SideStore"; + let is_sidestore = config.force_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 => { @@ -255,7 +257,7 @@ pub async fn sideload_app( let group_identifier = format!( "group.{}", - if config.force_sidestore_app_group { + if config.force_sidestore { format!("com.SideStore.SideStore.{}", team.team_id) } else { main_app_id_str.clone() @@ -321,7 +323,7 @@ pub async fn sideload_app( matching_app_groups[0].clone() }; - //let mut provisioning_profiles: HashMap = HashMap::new(); + let mut provisioning_profiles: HashMap = HashMap::new(); for app_id in app_ids { let assign_res = dev_session .assign_application_group_to_app_id( @@ -334,37 +336,21 @@ pub async fn sideload_app( if assign_res.is_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? - // .download_team_provisioning_profile(DeveloperDeviceType::Ios, &team, &main_app_id) - // .await - // { - // Ok(pp /* tee hee */) => pp, - // Err(e) => { - // return emit_error_and_return( - // &window, - // &format!("Failed to download provisioning profile: {:?}", e), - // ); - // } - // }; - // provisioning_profiles.insert(app_id.identifier.clone(), provisioning_profile); + let provisioning_profile = match dev_session + .download_team_provisioning_profile(DeveloperDeviceType::Ios, &team, &app_id) + .await + { + Ok(pp /* tee hee */) => pp, + Err(e) => { + return error_and_return(logger, e); + } + }; + provisioning_profiles.insert(app_id.identifier.clone(), provisioning_profile); } logger.log("Successfully registered app groups"); - let provisioning_profile = match dev_session - .download_team_provisioning_profile(DeveloperDeviceType::Ios, &team, &main_app_id) - .await - { - Ok(pp /* tee hee */) => pp, - Err(e) => { - return error_and_return(logger, e); - } - }; - - let profile_path = config - .store_dir - .join(format!("{}.mobileprovision", main_app_id_str)); + let profile_path = app.bundle.bundle_dir.join("embedded.mobileprovision"); if profile_path.exists() { std::fs::remove_file(&profile_path).map_err(Error::Filesystem)?; @@ -386,37 +372,53 @@ pub async fn sideload_app( ext.write_info()?; } - // 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, Error::ZSignError(e)); - // } - // }; + // Collect owned bundle identifiers and directories so we don't capture `app` or `logger` by reference in the blocking thread. + let embedded_bundles_info: Vec<(String, PathBuf)> = app + .bundle + .embedded_bundles() + .iter() + .map(|bundle| { + ( + bundle.bundle_identifier().unwrap_or("Unknown").to_string(), + bundle.bundle_dir.clone(), + ) + }) + .collect(); + let main_bundle_dir = app.bundle.bundle_dir.clone(); - let mut signer = BundleSigner::new_from_path(&app.bundle.bundle_dir) - .map_err(|e| Error::AppleCodesignError(Box::new(e)))?; + // Log bundle signing messages outside the blocking closure to avoid capturing non-'static references. + for (id, _) in &embedded_bundles_info { + logger.log(&format!("Signing bundle: {}", id)); + } - signer - .collect_nested_bundles() - .map_err(|e| Error::AppleCodesignError(Box::new(e)))?; + // Move owned data (cert, provisioning_profile, embedded_bundles_info) into the blocking task. + tokio::task::spawn_blocking(move || { + for (_id, bundle_dir) in embedded_bundles_info { + // Recreate settings for each bundle so ownership is clear and we don't move settings across iterations. + let mut settings = cert.to_signing_settings()?; + settings + .set_entitlements_xml( + SettingsScope::Main, + provisioning_profile.entitlements_xml()?, + ) + .map_err(|e| Error::AppleCodesignError(Box::new(e)))?; - let mut settings = SigningSettings::default(); + let signer = UnifiedSigner::new(settings); - settings.set_signing_key(cert.private_key.clone(), cert.certificate.unwrap()); + signer + .sign_path_in_place(&bundle_dir) + .map_err(|e| Error::AppleCodesignError(Box::new(e)))?; + } + Ok::<(), Error>(()) + }) + .await + .map_err(|e| Error::Generic(format!("Signing task failed: {}", e)))??; - signer - .write_signed_bundle(&app.bundle.bundle_dir, &settings) - .map_err(|e| Error::AppleCodesignError(Box::new(e)))?; - logger.log("App signed!"); + logger.log("Sucessfully signed app"); - logger.log("Installing app (Transfer)... 0%"); + logger.log("Installing app... 0%"); - let res = install_app(device_provider, &app.bundle.bundle_dir, |percentage| { + let res = install_app(device_provider, &main_bundle_dir, |percentage| { logger.log(&format!("Installing app... {}%", percentage)); }) .await; @@ -424,12 +426,12 @@ pub async fn sideload_app( return error_and_return(logger, e); } - if config.revoke_cert { - dev_session - .revoke_development_cert(DeveloperDeviceType::Ios, &team, &cert.get_serial_number()?) - .await?; - logger.log("Certificate revoked"); - } + // if config.revoke_cert { + // dev_session + // .revoke_development_cert(DeveloperDeviceType::Ios, &team, &cert.get_serial_number()?) + // .await?; + // logger.log("Certificate revoked"); + // } Ok(()) } From 8f44e8e3a19734c13c7c52ca0bf5b5220701d03d Mon Sep 17 00:00:00 2001 From: nab138 Date: Tue, 16 Dec 2025 07:50:38 -0500 Subject: [PATCH 03/71] Start isideload-next --- .github/workflows/build.yml | 13 +- Cargo.lock | 5643 ---------------------------- Cargo.toml | 2 +- LICENSE | 21 + README.md | 4 +- examples/minimal/.gitignore | 5 - examples/minimal/Cargo.toml | 10 - examples/minimal/src/main.rs | 68 - isideload/Cargo.toml | 20 +- isideload/src/application.rs | 75 - isideload/src/bundle.rs | 216 -- isideload/src/certificate.rs | 301 -- isideload/src/developer_session.rs | 779 ---- isideload/src/device.rs | 91 - isideload/src/lib.rs | 127 +- isideload/src/sideload.rs | 463 --- 16 files changed, 40 insertions(+), 7798 deletions(-) create mode 100644 LICENSE delete mode 100644 examples/minimal/.gitignore delete mode 100644 examples/minimal/Cargo.toml delete mode 100644 examples/minimal/src/main.rs delete mode 100644 isideload/src/application.rs delete mode 100644 isideload/src/bundle.rs delete mode 100644 isideload/src/certificate.rs delete mode 100644 isideload/src/developer_session.rs delete mode 100644 isideload/src/device.rs delete mode 100644 isideload/src/sideload.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7b0be6c..2dde64a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,8 +15,9 @@ jobs: fail-fast: false matrix: include: - - platform: "ubuntu-22.04" + - platform: "ubuntu-latest" - platform: "windows-latest" + - platform: "macos-latest" runs-on: ${{ matrix.platform }} steps: @@ -29,16 +30,12 @@ jobs: ~/.cargo/registry ~/.cargo/git target - key: ${{ runner.os }}-tauri-${{ hashFiles('Cargo.lock') }} + key: ${{ runner.os }}-${{ hashFiles('Cargo.lock') }} restore-keys: | - ${{ runner.os }}-tauri- + ${{ runner.os }}- - name: Install Rust stable uses: dtolnay/rust-toolchain@stable - - name: Add MSVC to PATH - if: matrix.platform == 'windows-latest' - uses: ilammy/msvc-dev-cmd@v1 - - name: Build - run: cargo build --features "vendored-openssl" + run: cargo build diff --git a/Cargo.lock b/Cargo.lock index cb09758..dafa1e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,5649 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array 0.14.9", -] - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "aes-gcm" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anstream" -version = "0.6.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" - -[[package]] -name = "anstyle-parse" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys 0.61.2", -] - -[[package]] -name = "anyhow" -version = "1.0.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" - -[[package]] -name = "app-store-connect" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed88de4349fc3eb58529530edeb7177516c94712d3dc8bf11bd76ac1715695c4" -dependencies = [ - "anyhow", - "base64 0.22.1", - "clap", - "dirs", - "env_logger", - "jsonwebtoken", - "log", - "pem", - "rand 0.8.5", - "reqwest 0.12.24", - "rsa", - "serde", - "serde_json", - "thiserror 2.0.17", - "x509-certificate", -] - -[[package]] -name = "apple-bundles" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f40bb8f844cec39fa3aceae717808c2ac3d2b6c474a9dffbeba07a4a945d10" -dependencies = [ - "anyhow", - "plist", - "simple-file-manifest", - "walkdir", -] - -[[package]] -name = "apple-codesign" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f24e9ebdb70a2aee3ca1cea217009fb50776955f0d7678c31d22e48c1524667f" -dependencies = [ - "anyhow", - "app-store-connect", - "apple-bundles", - "apple-flat-package", - "apple-xar", - "aws-config", - "aws-sdk-s3", - "aws-smithy-http 0.60.12", - "aws-smithy-types", - "base64 0.22.1", - "bcder", - "bitflags 2.10.0", - "bytes", - "chrono", - "clap", - "cryptographic-message-syntax", - "der 0.7.10", - "dialoguer", - "difference", - "digest", - "dirs", - "elliptic-curve 0.13.8", - "env_logger", - "figment", - "filetime", - "glob", - "goblin", - "hex", - "log", - "md-5", - "minicbor", - "num-traits", - "object", - "oid-registry", - "once_cell", - "p12", - "p256 0.13.2", - "pem", - "pkcs1", - "pkcs8 0.10.2", - "plist", - "rand 0.8.5", - "rasn", - "rayon", - "regex", - "reqwest 0.12.24", - "ring", - "rsa", - "scroll", - "security-framework 2.11.1", - "security-framework-sys", - "semver", - "serde", - "serde_json", - "serde_yaml", - "sha2", - "signature 2.2.0", - "simple-file-manifest", - "spake2", - "spki 0.7.3", - "subtle", - "tempfile", - "thiserror 2.0.17", - "tokio", - "tungstenite 0.24.0", - "uuid", - "walkdir", - "widestring", - "windows-sys 0.59.0", - "x509", - "x509-certificate", - "xml-rs", - "yasna", - "zeroize", - "zip 2.4.2", - "zip_structs", -] - -[[package]] -name = "apple-flat-package" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9d5a1fd8af4a376cc33d7e816a13f8ce127d52101f5dbc8061fb595397bea0" -dependencies = [ - "apple-xar", - "cpio-archive", - "flate2", - "scroll", - "serde", - "serde-xml-rs", - "thiserror 2.0.17", -] - -[[package]] -name = "apple-xar" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9631e781df71ebd049d7b4988cdae88712324cb20eb127fd79026bc8f1335d93" -dependencies = [ - "base64 0.22.1", - "bcder", - "bzip2", - "chrono", - "cryptographic-message-syntax", - "digest", - "flate2", - "log", - "md-5", - "rand 0.8.5", - "reqwest 0.12.24", - "scroll", - "serde", - "serde-xml-rs", - "sha1", - "sha2", - "signature 2.2.0", - "thiserror 2.0.17", - "url", - "x509-certificate", - "xml-rs", - "xz2", -] - -[[package]] -name = "arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" -dependencies = [ - "derive_arbitrary", -] - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "asn1-rs" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" -dependencies = [ - "asn1-rs-derive", - "asn1-rs-impl", - "displaydoc", - "nom", - "num-traits", - "rusticata-macros", - "thiserror 1.0.69", -] - -[[package]] -name = "asn1-rs-derive" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "asn1-rs-impl" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "async-compression" -version = "0.4.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a89bce6054c720275ac2432fbba080a66a2106a44a1b804553930ca6909f4e0" -dependencies = [ - "compression-codecs", - "compression-core", - "futures-core", - "futures-io", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "async_zip" -version = "0.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c50d65ce1b0e0cb65a785ff615f78860d7754290647d3b983208daa4f85e6" -dependencies = [ - "async-compression", - "crc32fast", - "futures-lite", - "pin-project", - "thiserror 2.0.17", - "tokio", - "tokio-util", -] - -[[package]] -name = "atomic" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "aws-config" -version = "1.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0149602eeaf915158e14029ba0c78dedb8c08d554b024d54c8f239aab46511d" -dependencies = [ - "aws-credential-types", - "aws-runtime", - "aws-sdk-sso", - "aws-sdk-ssooidc", - "aws-sdk-sts", - "aws-smithy-async", - "aws-smithy-http 0.62.5", - "aws-smithy-json", - "aws-smithy-runtime", - "aws-smithy-runtime-api", - "aws-smithy-types", - "aws-types", - "bytes", - "fastrand", - "hex", - "http 1.3.1", - "ring", - "time", - "tokio", - "tracing", - "url", - "zeroize", -] - -[[package]] -name = "aws-credential-types" -version = "1.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b01c9521fa01558f750d183c8c68c81b0155b9d193a4ba7f84c36bd1b6d04a06" -dependencies = [ - "aws-smithy-async", - "aws-smithy-runtime-api", - "aws-smithy-types", - "zeroize", -] - -[[package]] -name = "aws-lc-rs" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5932a7d9d28b0d2ea34c6b3779d35e3dd6f6345317c34e73438c4f1f29144151" -dependencies = [ - "aws-lc-sys", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1826f2e4cfc2cd19ee53c42fbf68e2f81ec21108e0b7ecf6a71cf062137360fc" -dependencies = [ - "bindgen", - "cc", - "cmake", - "dunce", - "fs_extra", -] - -[[package]] -name = "aws-runtime" -version = "1.5.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ce527fb7e53ba9626fc47824f25e256250556c40d8f81d27dd92aa38239d632" -dependencies = [ - "aws-credential-types", - "aws-sigv4", - "aws-smithy-async", - "aws-smithy-eventstream", - "aws-smithy-http 0.62.5", - "aws-smithy-runtime", - "aws-smithy-runtime-api", - "aws-smithy-types", - "aws-types", - "bytes", - "fastrand", - "http 0.2.12", - "http-body 0.4.6", - "percent-encoding", - "pin-project-lite", - "tracing", - "uuid", -] - -[[package]] -name = "aws-sdk-s3" -version = "1.115.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdaa0053cbcbc384443dd24569bd5d1664f86427b9dc04677bd0ab853954baec" -dependencies = [ - "aws-credential-types", - "aws-runtime", - "aws-sigv4", - "aws-smithy-async", - "aws-smithy-checksums", - "aws-smithy-eventstream", - "aws-smithy-http 0.62.5", - "aws-smithy-json", - "aws-smithy-runtime", - "aws-smithy-runtime-api", - "aws-smithy-types", - "aws-smithy-xml", - "aws-types", - "bytes", - "fastrand", - "hex", - "hmac", - "http 0.2.12", - "http 1.3.1", - "http-body 0.4.6", - "lru", - "percent-encoding", - "regex-lite", - "sha2", - "tracing", - "url", -] - -[[package]] -name = "aws-sdk-sso" -version = "1.90.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f18e53542c522459e757f81e274783a78f8c81acdfc8d1522ee8a18b5fb1c66" -dependencies = [ - "aws-credential-types", - "aws-runtime", - "aws-smithy-async", - "aws-smithy-http 0.62.5", - "aws-smithy-json", - "aws-smithy-runtime", - "aws-smithy-runtime-api", - "aws-smithy-types", - "aws-types", - "bytes", - "fastrand", - "http 0.2.12", - "regex-lite", - "tracing", -] - -[[package]] -name = "aws-sdk-ssooidc" -version = "1.92.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532f4d866012ffa724a4385c82e8dd0e59f0ca0e600f3f22d4c03b6824b34e4a" -dependencies = [ - "aws-credential-types", - "aws-runtime", - "aws-smithy-async", - "aws-smithy-http 0.62.5", - "aws-smithy-json", - "aws-smithy-runtime", - "aws-smithy-runtime-api", - "aws-smithy-types", - "aws-types", - "bytes", - "fastrand", - "http 0.2.12", - "regex-lite", - "tracing", -] - -[[package]] -name = "aws-sdk-sts" -version = "1.94.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1be6fbbfa1a57724788853a623378223fe828fc4c09b146c992f0c95b6256174" -dependencies = [ - "aws-credential-types", - "aws-runtime", - "aws-smithy-async", - "aws-smithy-http 0.62.5", - "aws-smithy-json", - "aws-smithy-query", - "aws-smithy-runtime", - "aws-smithy-runtime-api", - "aws-smithy-types", - "aws-smithy-xml", - "aws-types", - "fastrand", - "http 0.2.12", - "regex-lite", - "tracing", -] - -[[package]] -name = "aws-sigv4" -version = "1.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c35452ec3f001e1f2f6db107b6373f1f48f05ec63ba2c5c9fa91f07dad32af11" -dependencies = [ - "aws-credential-types", - "aws-smithy-eventstream", - "aws-smithy-http 0.62.5", - "aws-smithy-runtime-api", - "aws-smithy-types", - "bytes", - "crypto-bigint 0.5.5", - "form_urlencoded", - "hex", - "hmac", - "http 0.2.12", - "http 1.3.1", - "p256 0.11.1", - "percent-encoding", - "ring", - "sha2", - "subtle", - "time", - "tracing", - "zeroize", -] - -[[package]] -name = "aws-smithy-async" -version = "1.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "127fcfad33b7dfc531141fda7e1c402ac65f88aca5511a4d31e2e3d2cd01ce9c" -dependencies = [ - "futures-util", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "aws-smithy-checksums" -version = "0.63.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95bd108f7b3563598e4dc7b62e1388c9982324a2abd622442167012690184591" -dependencies = [ - "aws-smithy-http 0.62.5", - "aws-smithy-types", - "bytes", - "crc-fast", - "hex", - "http 0.2.12", - "http-body 0.4.6", - "md-5", - "pin-project-lite", - "sha1", - "sha2", - "tracing", -] - -[[package]] -name = "aws-smithy-eventstream" -version = "0.60.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e29a304f8319781a39808847efb39561351b1bb76e933da7aa90232673638658" -dependencies = [ - "aws-smithy-types", - "bytes", - "crc32fast", -] - -[[package]] -name = "aws-smithy-http" -version = "0.60.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7809c27ad8da6a6a68c454e651d4962479e81472aa19ae99e59f9aba1f9713cc" -dependencies = [ - "aws-smithy-runtime-api", - "aws-smithy-types", - "bytes", - "bytes-utils", - "futures-core", - "http 0.2.12", - "http-body 0.4.6", - "once_cell", - "percent-encoding", - "pin-project-lite", - "pin-utils", - "tracing", -] - -[[package]] -name = "aws-smithy-http" -version = "0.62.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445d5d720c99eed0b4aa674ed00d835d9b1427dd73e04adaf2f94c6b2d6f9fca" -dependencies = [ - "aws-smithy-eventstream", - "aws-smithy-runtime-api", - "aws-smithy-types", - "bytes", - "bytes-utils", - "futures-core", - "futures-util", - "http 0.2.12", - "http 1.3.1", - "http-body 0.4.6", - "percent-encoding", - "pin-project-lite", - "pin-utils", - "tracing", -] - -[[package]] -name = "aws-smithy-http-client" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "623254723e8dfd535f566ee7b2381645f8981da086b5c4aa26c0c41582bb1d2c" -dependencies = [ - "aws-smithy-async", - "aws-smithy-runtime-api", - "aws-smithy-types", - "h2 0.3.27", - "h2 0.4.12", - "http 0.2.12", - "http 1.3.1", - "http-body 0.4.6", - "hyper 0.14.32", - "hyper 1.8.1", - "hyper-rustls 0.24.2", - "hyper-rustls 0.27.7", - "hyper-util", - "pin-project-lite", - "rustls 0.21.12", - "rustls 0.23.35", - "rustls-native-certs 0.8.2", - "rustls-pki-types", - "tokio", - "tokio-rustls 0.26.4", - "tower", - "tracing", -] - -[[package]] -name = "aws-smithy-json" -version = "0.61.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2db31f727935fc63c6eeae8b37b438847639ec330a9161ece694efba257e0c54" -dependencies = [ - "aws-smithy-types", -] - -[[package]] -name = "aws-smithy-observability" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d1881b1ea6d313f9890710d65c158bdab6fb08c91ea825f74c1c8c357baf4cc" -dependencies = [ - "aws-smithy-runtime-api", -] - -[[package]] -name = "aws-smithy-query" -version = "0.60.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d28a63441360c477465f80c7abac3b9c4d075ca638f982e605b7dc2a2c7156c9" -dependencies = [ - "aws-smithy-types", - "urlencoding", -] - -[[package]] -name = "aws-smithy-runtime" -version = "1.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bbe9d018d646b96c7be063dd07987849862b0e6d07c778aad7d93d1be6c1ef0" -dependencies = [ - "aws-smithy-async", - "aws-smithy-http 0.62.5", - "aws-smithy-http-client", - "aws-smithy-observability", - "aws-smithy-runtime-api", - "aws-smithy-types", - "bytes", - "fastrand", - "http 0.2.12", - "http 1.3.1", - "http-body 0.4.6", - "http-body 1.0.1", - "pin-project-lite", - "pin-utils", - "tokio", - "tracing", -] - -[[package]] -name = "aws-smithy-runtime-api" -version = "1.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7204f9fd94749a7c53b26da1b961b4ac36bf070ef1e0b94bb09f79d4f6c193" -dependencies = [ - "aws-smithy-async", - "aws-smithy-types", - "bytes", - "http 0.2.12", - "http 1.3.1", - "pin-project-lite", - "tokio", - "tracing", - "zeroize", -] - -[[package]] -name = "aws-smithy-types" -version = "1.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25f535879a207fce0db74b679cfc3e91a3159c8144d717d55f5832aea9eef46e" -dependencies = [ - "base64-simd", - "bytes", - "bytes-utils", - "futures-core", - "http 0.2.12", - "http 1.3.1", - "http-body 0.4.6", - "http-body 1.0.1", - "http-body-util", - "itoa", - "num-integer", - "pin-project-lite", - "pin-utils", - "ryu", - "serde", - "time", - "tokio", - "tokio-util", -] - -[[package]] -name = "aws-smithy-xml" -version = "0.60.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab77cdd036b11056d2a30a7af7b775789fb024bf216acc13884c6c97752ae56" -dependencies = [ - "xmlparser", -] - -[[package]] -name = "aws-types" -version = "1.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d79fb68e3d7fe5d4833ea34dc87d2e97d26d3086cb3da660bb6b1f76d98680b6" -dependencies = [ - "aws-credential-types", - "aws-smithy-async", - "aws-smithy-runtime-api", - "aws-smithy-types", - "rustc_version", - "tracing", -] - -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64-simd" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" -dependencies = [ - "outref", - "vsimd", -] - -[[package]] -name = "base64ct" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" - -[[package]] -name = "bcder" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f7c42c9913f68cf9390a225e81ad56a5c515347287eb98baa710090ca1de86d" -dependencies = [ - "bytes", - "smallvec", -] - -[[package]] -name = "bindgen" -version = "0.72.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" -dependencies = [ - "bitflags 2.10.0", - "cexpr", - "clang-sys", - "itertools", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "bitvec-nom2" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d988fcc40055ceaa85edc55875a08f8abd29018582647fd82ad6128dba14a5f0" -dependencies = [ - "bitvec", - "nom", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array 0.14.9", -] - -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array 0.14.9", -] - -[[package]] -name = "bumpalo" -version = "3.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" - -[[package]] -name = "bytemuck" -version = "1.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" - -[[package]] -name = "bytes-utils" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" -dependencies = [ - "bytes", - "either", -] - -[[package]] -name = "bytesize" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e93abca9e28e0a1b9877922aacb20576e05d4679ffa78c3d6dc22a26a216659" - -[[package]] -name = "bzip2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" -dependencies = [ - "bzip2-sys", - "libc", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.13+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" -dependencies = [ - "cc", - "pkg-config", -] - -[[package]] -name = "cbc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher", -] - -[[package]] -name = "cc" -version = "1.2.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex", -] - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chrono" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-link", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "clap" -version = "4.5.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" - -[[package]] -name = "cmake" -version = "0.1.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" -dependencies = [ - "cc", -] - -[[package]] -name = "colorchoice" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" - -[[package]] -name = "compression-codecs" -version = "0.4.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8a506ec4b81c460798f572caead636d57d3d7e940f998160f52bd254bf2d23" -dependencies = [ - "compression-core", - "flate2", - "memchr", -] - -[[package]] -name = "compression-core" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47641d3deaf41fb1538ac1f54735925e275eaf3bf4d55c81b137fba797e5cbb" - -[[package]] -name = "console" -version = "0.15.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" -dependencies = [ - "encode_unicode", - "libc", - "once_cell", - "unicode-width", - "windows-sys 0.59.0", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "const_panic" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e262cdaac42494e3ae34c43969f9cdeb7da178bdb4b66fa6a1ea2edb4c8ae652" -dependencies = [ - "typewit", -] - -[[package]] -name = "cookie-factory" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" -dependencies = [ - "futures", -] - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpio-archive" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11d34b07689c21889fc89bd7cc885b3244b0157bbededf4a1c159832cd0df05" -dependencies = [ - "chrono", - "is_executable", - "simple-file-manifest", - "thiserror 1.0.69", -] - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crc" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - -[[package]] -name = "crc-fast" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ddc2d09feefeee8bd78101665bd8645637828fa9317f9f292496dbbd8c65ff3" -dependencies = [ - "crc", - "digest", - "rand 0.9.2", - "regex", - "rustversion", -] - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crypto-bigint" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" -dependencies = [ - "generic-array 0.14.9", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array 0.14.9", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array 0.14.9", - "rand_core 0.6.4", - "typenum", -] - -[[package]] -name = "cryptographic-message-syntax" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a99e58d7755c646cb3f2a138d99f90da4c495282e1700b82daff8a48759ce0" -dependencies = [ - "bcder", - "bytes", - "chrono", - "hex", - "pem", - "reqwest 0.12.24", - "ring", - "signature 2.2.0", - "x509-certificate", -] - -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "fiat-crypto", - "rand_core 0.6.4", - "rustc_version", - "subtle", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "data-encoding" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" - -[[package]] -name = "der" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "der_derive", - "flagset", - "pem-rfc7468", - "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", -] - -[[package]] -name = "deranged" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "derive_arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "des" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" -dependencies = [ - "cipher", -] - -[[package]] -name = "dialoguer" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" -dependencies = [ - "console", - "shell-words", - "tempfile", - "thiserror 1.0.69", - "zeroize", -] - -[[package]] -name = "difference" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - -[[package]] -name = "ecdsa" -version = "0.14.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" -dependencies = [ - "der 0.6.1", - "elliptic-curve 0.12.3", - "rfc6979", - "signature 1.6.4", -] - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der 0.7.10", - "digest", - "elliptic-curve 0.13.8", - "signature 2.2.0", - "spki 0.7.3", -] - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "elliptic-curve" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" -dependencies = [ - "base16ct 0.1.1", - "crypto-bigint 0.4.9", - "der 0.6.1", - "digest", - "ff 0.12.1", - "generic-array 0.14.9", - "group 0.12.1", - "pkcs8 0.9.0", - "rand_core 0.6.4", - "sec1 0.3.0", - "subtle", - "zeroize", -] - -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct 0.2.0", - "crypto-bigint 0.5.5", - "digest", - "ff 0.13.1", - "generic-array 0.14.9", - "group 0.13.0", - "pkcs8 0.10.2", - "rand_core 0.6.4", - "sec1 0.7.3", - "subtle", - "zeroize", -] - -[[package]] -name = "encode_unicode" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" - -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "env_filter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.11.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" -dependencies = [ - "anstream", - "anstyle", - "env_filter", - "jiff", - "log", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "ff" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "ff" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - -[[package]] -name = "figment" -version = "0.10.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" -dependencies = [ - "atomic", - "pear", - "serde", - "toml", - "uncased", - "version_check", -] - -[[package]] -name = "filetime" -version = "0.2.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" -dependencies = [ - "cfg-if", - "libc", - "libredox", - "windows-sys 0.60.2", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" -dependencies = [ - "crc32fast", - "libz-rs-sys", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-lite" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "generic-array" -version = "1.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf57c49a95fd1fe24b90b3033bee6dc7e8f1288d51494cb44e627c295e38542" -dependencies = [ - "rustversion", - "typenum", -] - -[[package]] -name = "getrandom" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "wasip2", - "wasm-bindgen", -] - -[[package]] -name = "ghash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" -dependencies = [ - "opaque-debug", - "polyval", -] - -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - -[[package]] -name = "goblin" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daa0a64d21a7eb230583b4c5f4e23b7e4e57974f96620f42a7e75e08ae66d745" -dependencies = [ - "log", - "plain", - "scroll", -] - -[[package]] -name = "group" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" -dependencies = [ - "ff 0.12.1", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff 0.13.1", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "h2" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "h2" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http 1.3.1", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http 1.3.1", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http 1.3.1", - "http-body 1.0.1", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "0.14.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.3.27", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2 0.5.10", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "h2 0.4.12", - "http 1.3.1", - "http-body 1.0.1", - "httparse", - "itoa", - "pin-project-lite", - "pin-utils", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" -dependencies = [ - "futures-util", - "http 0.2.12", - "hyper 0.14.32", - "log", - "rustls 0.21.12", - "rustls-native-certs 0.6.3", - "tokio", - "tokio-rustls 0.24.1", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" -dependencies = [ - "http 1.3.1", - "hyper 1.8.1", - "hyper-util", - "rustls 0.23.35", - "rustls-native-certs 0.8.2", - "rustls-pki-types", - "tokio", - "tokio-rustls 0.26.4", - "tower-service", - "webpki-roots 1.0.4", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper 0.14.32", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "hyper-util" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http 1.3.1", - "http-body 1.0.1", - "hyper 1.8.1", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2 0.6.1", - "tokio", - "tower-service", - "tracing", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" -dependencies = [ - "displaydoc", - "potential_utf", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" - -[[package]] -name = "icu_properties" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" - -[[package]] -name = "icu_provider" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "idevice" -version = "0.1.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "893625fcbcb100760f15691e634a845e5ec7f5e6eb75acbc3d5d1c5678bd1738" -dependencies = [ - "async_zip", - "base64 0.22.1", - "chrono", - "futures", - "plist", - "rustls 0.23.35", - "serde", - "thiserror 2.0.17", - "tokio", - "tokio-rustls 0.26.4", - "tracing", -] - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" -dependencies = [ - "equivalent", - "hashbrown 0.16.0", -] - -[[package]] -name = "inlinable_string" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" - -[[package]] -name = "inout" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" -dependencies = [ - "block-padding", - "generic-array 0.14.9", -] - -[[package]] -name = "ipnet" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" - -[[package]] -name = "iri-string" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "is_executable" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baabb8b4867b26294d818bf3f651a454b6901431711abb96e296245888d6e8c4" -dependencies = [ - "windows-sys 0.60.2", -] - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - [[package]] name = "isideload" -version = "0.1.21" -dependencies = [ - "apple-codesign", - "hex", - "idevice", - "nab138_icloud_auth", - "p12", - "plist", - "rand 0.8.5", - "rcgen", - "rsa", - "serde", - "sha1", - "thiserror 2.0.17", - "tokio", - "uuid", - "x509-certificate", - "zip 4.6.1", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" - -[[package]] -name = "jiff" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" -dependencies = [ - "jiff-static", - "log", - "portable-atomic", - "portable-atomic-util", - "serde_core", -] - -[[package]] -name = "jiff-static" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "jsonwebtoken" -version = "9.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" -dependencies = [ - "base64 0.22.1", - "js-sys", - "pem", - "ring", - "serde", - "serde_json", - "simple_asn1", -] - -[[package]] -name = "konst" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4381b9b00c55f251f2ebe9473aef7c117e96828def1a7cb3bd3f0f903c6894e9" -dependencies = [ - "const_panic", - "konst_kernel", - "typewit", -] - -[[package]] -name = "konst_kernel" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4b1eb7788f3824c629b1116a7a9060d6e898c358ebff59070093d51103dcc3c" -dependencies = [ - "typewit", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] - -[[package]] -name = "libc" -version = "0.2.177" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" - -[[package]] -name = "libloading" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" -dependencies = [ - "cfg-if", - "windows-link", -] - -[[package]] -name = "libm" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" - -[[package]] -name = "libredox" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" -dependencies = [ - "bitflags 2.10.0", - "libc", - "redox_syscall", -] - -[[package]] -name = "libz-rs-sys" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "840db8cf39d9ec4dd794376f38acc40d0fc65eec2a8f484f7fd375b84602becd" -dependencies = [ - "zlib-rs", -] - -[[package]] -name = "linux-raw-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" - -[[package]] -name = "litemap" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" - -[[package]] -name = "log" -version = "0.4.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" - -[[package]] -name = "lru" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" -dependencies = [ - "hashbrown 0.15.5", -] - -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - -[[package]] -name = "lzma-sys" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "md-5" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" -dependencies = [ - "cfg-if", - "digest", -] - -[[package]] -name = "memchr" -version = "2.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "minicbor" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0452a60c1863c1f50b5f77cd295e8d2786849f35883f0b9e18e7e6e1b5691b0" -dependencies = [ - "minicbor-derive", -] - -[[package]] -name = "minicbor-derive" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd2209fff77f705b00c737016a48e73733d7fbccb8b007194db148f03561fb70" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "minimal" -version = "0.1.0" -dependencies = [ - "idevice", - "isideload", - "tokio", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "nab138_icloud_auth" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2707d665f2ceb5dfc1baf632fd708c6ffba2596f9e63fea054367d1b0e3b7317" -dependencies = [ - "aes", - "aes-gcm", - "base64 0.22.1", - "cbc", - "hmac", - "nab138_omnisette", - "nab138_srp", - "num-bigint", - "pbkdf2", - "pkcs7", - "plist", - "rand 0.9.2", - "reqwest 0.11.27", - "rustls 0.23.35", - "rustls-pemfile 2.2.0", - "serde", - "serde_json", - "sha2", - "thiserror 2.0.17", - "tokio", -] - -[[package]] -name = "nab138_omnisette" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322cfbcadd65524423b56558259aa093287f1d91bba3515a39de0a38b75d3d36" -dependencies = [ - "anyhow", - "async-trait", - "base64 0.22.1", - "chrono", - "futures-util", - "log", - "plist", - "rand 0.9.2", - "reqwest 0.11.27", - "serde", - "serde_json", - "sha2", - "thiserror 2.0.17", - "tokio-tungstenite", - "uuid", -] - -[[package]] -name = "nab138_srp" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "587a7a2ae38ab9a818f42c12b02a7ad5d738006f78f3b53a9f28da91fe13411d" -dependencies = [ - "base64 0.22.1", - "digest", - "generic-array 1.3.5", - "lazy_static", - "num-bigint", - "subtle", -] - -[[package]] -name = "native-tls" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework 2.11.1", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-bigint-dig" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" -dependencies = [ - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "smallvec", - "zeroize", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "object" -version = "0.36.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" -dependencies = [ - "crc32fast", - "flate2", - "hashbrown 0.15.5", - "indexmap", - "memchr", - "ruzstd", -] - -[[package]] -name = "oid-registry" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" -dependencies = [ - "asn1-rs", -] - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - -[[package]] -name = "openssl" -version = "0.10.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" -dependencies = [ - "bitflags 2.10.0", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - -[[package]] -name = "openssl-sys" -version = "0.9.111" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "option-ext" version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "outref" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" - -[[package]] -name = "p12" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4873306de53fe82e7e484df31e1e947d61514b6ea2ed6cd7b45d63006fd9224" -dependencies = [ - "cbc", - "cipher", - "des", - "getrandom 0.2.16", - "hmac", - "lazy_static", - "rc2", - "sha1", - "yasna", -] - -[[package]] -name = "p256" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" -dependencies = [ - "ecdsa 0.14.8", - "elliptic-curve 0.12.3", - "sha2", -] - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa 0.16.9", - "elliptic-curve 0.13.8", - "primeorder", -] - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "password-hash" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" -dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "pbkdf2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" -dependencies = [ - "digest", - "hmac", - "password-hash", - "sha2", -] - -[[package]] -name = "pear" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" -dependencies = [ - "inlinable_string", - "pear_codegen", - "yansi", -] - -[[package]] -name = "pear_codegen" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" -dependencies = [ - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn", -] - -[[package]] -name = "pem" -version = "3.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" -dependencies = [ - "base64 0.22.1", - "serde_core", -] - -[[package]] -name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pin-project" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der 0.7.10", - "pkcs8 0.10.2", - "spki 0.7.3", -] - -[[package]] -name = "pkcs7" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d79178be066405e0602bf3035946edef6b11b3f9dde46dfe5f8bfd7dea4b77e7" -dependencies = [ - "der 0.7.10", - "spki 0.7.3", - "x509-cert", -] - -[[package]] -name = "pkcs8" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" -dependencies = [ - "der 0.6.1", - "spki 0.6.0", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der 0.7.10", - "spki 0.7.3", -] - -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "plain" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" - -[[package]] -name = "plist" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" -dependencies = [ - "base64 0.22.1", - "indexmap", - "quick-xml", - "serde", - "time", -] - -[[package]] -name = "polyval" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "portable-atomic" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" - -[[package]] -name = "portable-atomic-util" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" -dependencies = [ - "portable-atomic", -] - -[[package]] -name = "potential_utf" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" -dependencies = [ - "zerovec", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn", -] - -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve 0.13.8", -] - -[[package]] -name = "proc-macro2" -version = "1.0.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "proc-macro2-diagnostics" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "version_check", - "yansi", -] - -[[package]] -name = "quick-xml" -version = "0.38.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" -dependencies = [ - "memchr", -] - -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls 0.23.35", - "socket2 0.6.1", - "thiserror 2.0.17", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" -dependencies = [ - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.2", - "ring", - "rustc-hash", - "rustls 0.23.35", - "rustls-pki-types", - "slab", - "thiserror 2.0.17", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2 0.6.1", - "tracing", - "windows-sys 0.60.2", -] - -[[package]] -name = "quote" -version = "1.0.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.16", -] - -[[package]] -name = "rand_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "rasn" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e442690f86da40561d5548e7ffb4a18af90d1c1b3536090de847ca2d5a3a6426" -dependencies = [ - "arrayvec", - "bitvec", - "bitvec-nom2", - "bytes", - "chrono", - "either", - "hashbrown 0.14.5", - "konst", - "nom", - "num-bigint", - "num-integer", - "num-traits", - "once_cell", - "rasn-derive", - "serde_json", - "snafu", -] - -[[package]] -name = "rasn-derive" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b0d374c7e4e985e6bc97ca7e7ad1d9642a8415db2017777d6e383002edaab2" -dependencies = [ - "either", - "itertools", - "proc-macro2", - "quote", - "rayon", - "syn", - "uuid", -] - -[[package]] -name = "rayon" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "rc2" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62c64daa8e9438b84aaae55010a93f396f8e60e3911590fcba770d04643fc1dd" -dependencies = [ - "cipher", -] - -[[package]] -name = "rcgen" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" -dependencies = [ - "pem", - "ring", - "rustls-pki-types", - "time", - "yasna", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.10.0", -] - -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom 0.2.16", - "libredox", - "thiserror 1.0.69", -] - -[[package]] -name = "regex" -version = "1.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-lite" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d942b98df5e658f56f20d592c7f868833fe38115e65c33003d8cd224b0155da" - -[[package]] -name = "regex-syntax" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" - -[[package]] -name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "async-compression", - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2 0.3.27", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.32", - "hyper-rustls 0.24.2", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls 0.21.12", - "rustls-pemfile 1.0.4", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration", - "tokio", - "tokio-native-tls", - "tokio-rustls 0.24.1", - "tokio-util", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots 0.25.4", - "winreg", -] - -[[package]] -name = "reqwest" -version = "0.12.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.4.12", - "http 1.3.1", - "http-body 1.0.1", - "http-body-util", - "hyper 1.8.1", - "hyper-rustls 0.27.7", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls 0.23.35", - "rustls-native-certs 0.8.2", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 1.0.2", - "tokio", - "tokio-rustls 0.26.4", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots 1.0.4", -] - -[[package]] -name = "rfc6979" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" -dependencies = [ - "crypto-bigint 0.4.9", - "hmac", - "zeroize", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.16", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rsa" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88" -dependencies = [ - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8 0.10.2", - "rand_core 0.6.4", - "signature 2.2.0", - "spki 0.7.3", - "subtle", - "zeroize", -] - -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rusticata-macros" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" -dependencies = [ - "nom", -] - -[[package]] -name = "rustix" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" -dependencies = [ - "bitflags 2.10.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls" -version = "0.21.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" -dependencies = [ - "log", - "ring", - "rustls-webpki 0.101.7", - "sct", -] - -[[package]] -name = "rustls" -version = "0.23.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" -dependencies = [ - "aws-lc-rs", - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki 0.103.8", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" -dependencies = [ - "openssl-probe", - "rustls-pemfile 1.0.4", - "schannel", - "security-framework 2.11.1", -] - -[[package]] -name = "rustls-native-certs" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" -dependencies = [ - "openssl-probe", - "rustls-pemfile 2.2.0", - "rustls-pki-types", - "schannel", - "security-framework 2.11.1", -] - -[[package]] -name = "rustls-native-certs" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" -dependencies = [ - "openssl-probe", - "rustls-pki-types", - "schannel", - "security-framework 3.5.1", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -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.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" -dependencies = [ - "web-time", - "zeroize", -] - -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "rustls-webpki" -version = "0.103.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" -dependencies = [ - "aws-lc-rs", - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "ruzstd" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad02996bfc73da3e301efe90b1837be9ed8f4a462b6ed410aa35d00381de89f" -dependencies = [ - "twox-hash", -] - -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "scroll" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6" -dependencies = [ - "scroll_derive", -] - -[[package]] -name = "scroll_derive" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "sec1" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" -dependencies = [ - "base16ct 0.1.1", - "der 0.6.1", - "generic-array 0.14.9", - "pkcs8 0.9.0", - "subtle", - "zeroize", -] - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct 0.2.0", - "der 0.7.10", - "generic-array 0.14.9", - "pkcs8 0.10.2", - "subtle", - "zeroize", -] - -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.10.0", - "core-foundation 0.9.4", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework" -version = "3.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" -dependencies = [ - "bitflags 2.10.0", - "core-foundation 0.10.1", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde-xml-rs" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb3aa78ecda1ebc9ec9847d5d3aba7d618823446a049ba2491940506da6e2782" -dependencies = [ - "log", - "serde", - "thiserror 1.0.69", - "xml-rs", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.145" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", - "serde_core", -] - -[[package]] -name = "serde_spanned" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_yaml" -version = "0.9.34+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" -dependencies = [ - "indexmap", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "shell-words" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook-registry" -version = "1.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" -dependencies = [ - "libc", -] - -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -dependencies = [ - "digest", - "rand_core 0.6.4", -] - -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core 0.6.4", -] - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "simple-file-manifest" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd19be0257552dd56d1bb6946f89f193c6e5b9f13cc9327c4bc84a357507c74" - -[[package]] -name = "simple_asn1" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" -dependencies = [ - "num-bigint", - "num-traits", - "thiserror 2.0.17", - "time", -] - -[[package]] -name = "slab" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "snafu" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e84b3f4eacbf3a1ce05eac6763b4d629d60cbc94d632e4092c54ade71f1e1a2" -dependencies = [ - "snafu-derive", -] - -[[package]] -name = "snafu-derive" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1c97747dbf44bb1ca44a561ece23508e99cb592e862f22222dcf42f51d1e451" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "socket2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" -dependencies = [ - "libc", - "windows-sys 0.60.2", -] - -[[package]] -name = "spake2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5482afe85a0b6ce956c945401598dbc527593c77ba51d0a87a586938b1b893a" -dependencies = [ - "curve25519-dalek", - "hkdf", - "rand_core 0.6.4", - "sha2", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "spki" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" -dependencies = [ - "base64ct", - "der 0.6.1", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der 0.7.10", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "2.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f17c7e013e88258aa9543dcbe81aca68a667a9ac37cd69c9fbc07858bfe0e2f" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.4", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" -dependencies = [ - "fastrand", - "getrandom 0.3.4", - "once_cell", - "rustix", - "windows-sys 0.61.2", -] - -[[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.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" -dependencies = [ - "thiserror-impl 2.0.17", -] - -[[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", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "time" -version = "0.3.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" - -[[package]] -name = "time-macros" -version = "0.2.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinystr" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" -dependencies = [ - "bytes", - "libc", - "mio", - "pin-project-lite", - "signal-hook-registry", - "socket2 0.6.1", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls 0.21.12", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls 0.23.35", - "tokio", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489a59b6730eda1b0171fcfda8b121f4bee2b35cba8645ca35c5f7ba3eb736c1" -dependencies = [ - "futures-util", - "log", - "rustls 0.23.35", - "rustls-pki-types", - "tokio", - "tokio-rustls 0.26.4", - "tungstenite 0.27.0", - "webpki-roots 0.26.11", -] - -[[package]] -name = "tokio-util" -version = "0.7.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" -dependencies = [ - "bytes", - "futures-core", - "futures-io", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.22.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "toml_write", - "winnow", -] - -[[package]] -name = "toml_write" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" - -[[package]] -name = "tower" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper 1.0.2", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" -dependencies = [ - "bitflags 2.10.0", - "bytes", - "futures-util", - "http 1.3.1", - "http-body 1.0.1", - "iri-string", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "tungstenite" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http 1.3.1", - "httparse", - "log", - "rand 0.8.5", - "rustls 0.23.35", - "rustls-native-certs 0.7.3", - "rustls-pki-types", - "sha1", - "thiserror 1.0.69", - "utf-8", -] - -[[package]] -name = "tungstenite" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadc29d668c91fcc564941132e17b28a7ceb2f3ebf0b9dae3e03fd7a6748eb0d" -dependencies = [ - "bytes", - "data-encoding", - "http 1.3.1", - "httparse", - "log", - "rand 0.9.2", - "rustls 0.23.35", - "rustls-pki-types", - "sha1", - "thiserror 2.0.17", - "utf-8", -] - -[[package]] -name = "twox-hash" -version = "1.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" -dependencies = [ - "cfg-if", - "static_assertions", -] - -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - -[[package]] -name = "typewit" -version = "1.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c1ae7cc0fdb8b842d65d127cb981574b0d2b249b74d1c7a2986863dc134f71" -dependencies = [ - "typewit_proc_macros", -] - -[[package]] -name = "typewit_proc_macros" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6" - -[[package]] -name = "uncased" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-ident" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" - -[[package]] -name = "unicode-width" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" - -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - -[[package]] -name = "unsafe-libyaml" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "uuid" -version = "1.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" -dependencies = [ - "getrandom 0.3.4", - "js-sys", - "rand 0.9.2", - "uuid-macro-internal", - "wasm-bindgen", -] - -[[package]] -name = "uuid-macro-internal" -version = "1.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9384a660318abfbd7f8932c34d67e4d1ec511095f95972ddc01e19d7ba8413f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "vsimd" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" -dependencies = [ - "cfg-if", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "web-sys" -version = "0.3.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[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.4", -] - -[[package]] -name = "webpki-roots" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "widestring" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - -[[package]] -name = "winnow" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "wit-bindgen" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" - -[[package]] -name = "writeable" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "x509" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3cec94c3999f31341553f358ef55f65fc031291a022cd42ec0ce7219560c76" -dependencies = [ - "chrono", - "cookie-factory", -] - -[[package]] -name = "x509-cert" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" -dependencies = [ - "const-oid", - "der 0.7.10", - "spki 0.7.3", -] - -[[package]] -name = "x509-certificate" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e57b9f8bcae7c1f36479821ae826d75050c60ce55146fd86d3553ed2573e2762" -dependencies = [ - "bcder", - "bytes", - "chrono", - "der 0.7.10", - "hex", - "pem", - "ring", - "signature 2.2.0", - "spki 0.7.3", - "thiserror 1.0.69", - "zeroize", -] - -[[package]] -name = "xml-rs" -version = "0.8.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" - -[[package]] -name = "xmlparser" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" - -[[package]] -name = "xz2" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" -dependencies = [ - "lzma-sys", -] - -[[package]] -name = "yansi" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" - -[[package]] -name = "yasna" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" -dependencies = [ - "time", -] - -[[package]] -name = "yoke" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.8.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zerotrie" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zip" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" -dependencies = [ - "arbitrary", - "crc32fast", - "crossbeam-utils", - "displaydoc", - "flate2", - "indexmap", - "memchr", - "thiserror 2.0.17", - "zopfli", -] - -[[package]] -name = "zip" -version = "4.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1" -dependencies = [ - "arbitrary", - "crc32fast", - "flate2", - "indexmap", - "memchr", - "zopfli", -] - -[[package]] -name = "zip_structs" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce824a6bfffe8942820fa36d24973b7c83a40896749a42e33de0abdd11750ee5" -dependencies = [ - "byteorder", - "bytesize", - "thiserror 1.0.69", -] - -[[package]] -name = "zlib-rs" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f06ae92f42f5e5c42443fd094f245eb656abf56dd7cce9b8b263236565e00f2" - -[[package]] -name = "zopfli" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" -dependencies = [ - "bumpalo", - "crc32fast", - "log", - "simd-adler32", -] diff --git a/Cargo.toml b/Cargo.toml index 9f614a4..08a856b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,4 @@ [workspace] resolver = "2" -members = ["isideload", "examples/minimal"] +members = ["isideload"] default-members = ["isideload"] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..78a8b6b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 nab138 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 048c601..5055363 100644 --- a/README.md +++ b/README.md @@ -96,13 +96,13 @@ See [examples/minimal/src/main.rs](examples/minimal/src/main.rs). ## Licensing -This project is licensed under the MPL-2.0 License. See the [LICENSE](LICENSE) file for details. +This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. ## Credits - The amazing [idevice](https://github.com/jkcoxson/idevice) crate is used to communicate with the device -- Packages from [`apple-private-apis`](https://github.com/SideStore/apple-private-apis) were used for authentication, but the original project was left unfinished. To support isideload, `apple-private-apis` was forked and modified to add missing features. With permission from the original developers, the fork was published to crates.io until the official project is published. +- Packages from [`apple-private-apis`](https://github.com/SideStore/apple-private-apis), which is licensed under MPL-2.0, were used for authentication, but the original project was left unfinished. To support isideload, `apple-private-apis` was [forked](https://github.com/nab138/apple-private-apis) and modified to add missing features. With permission from the original developers, the fork was published to crates.io until the official project is published. - [apple-codesign](https://crates.io/crates/apple-codesign) was used for code signing, which is licensed under MPL-2.0. diff --git a/examples/minimal/.gitignore b/examples/minimal/.gitignore deleted file mode 100644 index 7b09836..0000000 --- a/examples/minimal/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.zsign_cache -keys -*.ipa -state.plist -*.mobileprovision diff --git a/examples/minimal/Cargo.toml b/examples/minimal/Cargo.toml deleted file mode 100644 index 33e8b55..0000000 --- a/examples/minimal/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "minimal" -version = "0.1.0" -edition = "2024" -publish = false - -[dependencies] -isideload = { path = "../../isideload" } -idevice = { version = "0.1.46", features = ["usbmuxd", "ring"], default-features = false} -tokio = { version = "1.43", features = ["macros", "rt-multi-thread"] } diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs deleted file mode 100644 index 9b2e9a9..0000000 --- a/examples/minimal/src/main.rs +++ /dev/null @@ -1,68 +0,0 @@ -use std::{env, path::PathBuf, sync::Arc}; - -use idevice::usbmuxd::{UsbmuxdAddr, UsbmuxdConnection}; -use isideload::{ - AnisetteConfiguration, AppleAccount, SideloadConfiguration, - developer_session::DeveloperSession, sideload::sideload_app, -}; - -#[tokio::main] -async fn main() { - let args: Vec = env::args().collect(); - let app_path = PathBuf::from( - args.get(1) - .expect("Please provide the path to the app to install"), - ); - let apple_id = args - .get(2) - .expect("Please provide the Apple ID to use for installation"); - let apple_password = args.get(3).expect("Please provide the Apple ID password"); - - // You don't have to use usbmuxd, you can use any IdeviceProvider - let usbmuxd = UsbmuxdConnection::default().await; - if usbmuxd.is_err() { - panic!("Failed to connect to usbmuxd: {:?}", usbmuxd.err()); - } - let mut usbmuxd = usbmuxd.unwrap(); - - let devs = usbmuxd.get_devices().await.unwrap(); - if devs.is_empty() { - panic!("No devices found"); - } - - let provider = devs - .iter() - .next() - .unwrap() - .to_provider(UsbmuxdAddr::from_env_var().unwrap(), "isideload-demo"); - - // Change the anisette url and such here - // Note that right now only remote anisette servers are supported - let anisette_config = AnisetteConfiguration::default(); - - let get_2fa_code = || { - let mut code = String::new(); - println!("Enter 2FA code:"); - std::io::stdin().read_line(&mut code).unwrap(); - Ok(code.trim().to_string()) - }; - - let account = AppleAccount::login( - || Ok((apple_id.to_string(), apple_password.to_string())), - get_2fa_code, - anisette_config, - ) - .await - .unwrap(); - - let dev_session = DeveloperSession::new(Arc::new(account)); - - // You can change the machine name, store directory (for certs, anisette data, & provision files), and logger - let config = SideloadConfiguration::default() - .set_machine_name("isideload-demo".to_string()) - .set_force_sidestore(true); - - sideload_app(&provider, &dev_session, app_path, config) - .await - .unwrap() -} diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 1675b36..b3f0f0d 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "isideload" description = "Sideload iOS/iPadOS applications" -license = "MPL-2.0" +license = "MIT" authors = ["Nicholas Sharp "] -version = "0.1.21" +version = "0.2.0" edition = "2024" repository = "https://github.com/nab138/isideload" documentation = "https://docs.rs/isideload" @@ -14,19 +14,3 @@ readme = "../README.md" default = [] [dependencies] -serde = { version = "1", features = ["derive"] } -plist = { version = "1.7" } -icloud_auth = { version = "0.1.5", package = "nab138_icloud_auth" } -uuid = { version = "1.17.0", features = ["v4"] } -zip = { version = "4.3", default-features = false, features = ["deflate"] } -hex = "0.4" -sha1 = "0.10" -idevice = { version = "0.1.46", features = ["afc", "installation_proxy", "ring"], default-features = false } -thiserror = "2" -apple-codesign = "0.29.0" -x509-certificate = "0.24.0" -rsa = "0.9" -rcgen = "0.13" -rand = "0.8" -tokio = "1.48.0" -p12 = "0.6.3" diff --git a/isideload/src/application.rs b/isideload/src/application.rs deleted file mode 100644 index 50df046..0000000 --- a/isideload/src/application.rs +++ /dev/null @@ -1,75 +0,0 @@ -// This file was made using https://github.com/Dadoum/Sideloader as a reference. - -use crate::Error; -use crate::bundle::Bundle; -use std::fs::File; -use std::path::PathBuf; -use zip::ZipArchive; - -pub struct Application { - pub bundle: Bundle, - //pub temp_path: PathBuf, -} - -impl Application { - pub fn new(path: PathBuf) -> Result { - if !path.exists() { - return Err(Error::InvalidBundle( - "Application path does not exist".to_string(), - )); - } - - let mut bundle_path = path.clone(); - //let mut temp_path = PathBuf::new(); - - if path.is_file() { - let temp_dir = std::env::temp_dir(); - let temp_path = temp_dir - .join(path.file_name().unwrap().to_string_lossy().to_string() + "_extracted"); - if temp_path.exists() { - std::fs::remove_dir_all(&temp_path).map_err(Error::Filesystem)?; - } - std::fs::create_dir_all(&temp_path).map_err(Error::Filesystem)?; - - let file = File::open(&path).map_err(Error::Filesystem)?; - let mut archive = ZipArchive::new(file).map_err(|e| { - Error::Generic(format!("Failed to open application archive: {}", e)) - })?; - archive.extract(&temp_path).map_err(|e| { - Error::Generic(format!("Failed to extract application archive: {}", e)) - })?; - - let payload_folder = temp_path.join("Payload"); - if payload_folder.exists() && payload_folder.is_dir() { - let app_dirs: Vec<_> = std::fs::read_dir(&payload_folder) - .map_err(|e| { - Error::Generic(format!("Failed to read Payload directory: {}", e)) - })? - .filter_map(Result::ok) - .filter(|entry| entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false)) - .filter(|entry| entry.path().extension().is_some_and(|ext| ext == "app")) - .collect(); - if app_dirs.len() == 1 { - bundle_path = app_dirs[0].path(); - } else if app_dirs.is_empty() { - return Err(Error::InvalidBundle( - "No .app directory found in Payload".to_string(), - )); - } else { - return Err(Error::InvalidBundle( - "Multiple .app directories found in Payload".to_string(), - )); - } - } else { - return Err(Error::InvalidBundle( - "No Payload directory found in the application archive".to_string(), - )); - } - } - let bundle = Bundle::new(bundle_path)?; - - Ok(Application { - bundle, /*temp_path*/ - }) - } -} diff --git a/isideload/src/bundle.rs b/isideload/src/bundle.rs deleted file mode 100644 index c4fc36c..0000000 --- a/isideload/src/bundle.rs +++ /dev/null @@ -1,216 +0,0 @@ -// This file was made using https://github.com/Dadoum/Sideloader as a reference. - -use crate::Error; -use plist::{Dictionary, Value}; -use std::{ - fs, - path::{Path, PathBuf}, -}; - -#[derive(Debug, Clone)] -pub struct Bundle { - pub app_info: Dictionary, - pub bundle_dir: PathBuf, - pub bundle_type: BundleType, - app_extensions: Vec, - frameworks: Vec, - _libraries: Vec, -} - -impl Bundle { - pub fn new(bundle_dir: PathBuf) -> Result { - let mut bundle_path = bundle_dir; - // Remove trailing slash/backslash - if let Some(path_str) = bundle_path.to_str() - && (path_str.ends_with('/') || path_str.ends_with('\\')) - { - bundle_path = PathBuf::from(&path_str[..path_str.len() - 1]); - } - - let info_plist_path = bundle_path.join("Info.plist"); - assert_bundle( - info_plist_path.exists(), - &format!("No Info.plist here: {}", info_plist_path.display()), - )?; - - let plist_data = fs::read(&info_plist_path) - .map_err(|e| Error::InvalidBundle(format!("Failed to read Info.plist: {}", e)))?; - - let app_info = plist::from_bytes(&plist_data) - .map_err(|e| Error::InvalidBundle(format!("Failed to parse Info.plist: {}", e)))?; - - // Load app extensions from PlugIns directory - let plug_ins_dir = bundle_path.join("PlugIns"); - let app_extensions = if plug_ins_dir.exists() { - fs::read_dir(&plug_ins_dir) - .map_err(|e| { - Error::InvalidBundle(format!("Failed to read PlugIns directory: {}", e)) - })? - .filter_map(|entry| entry.ok()) - .filter(|entry| { - entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false) - && entry.path().join("Info.plist").exists() - }) - .filter_map(|entry| Bundle::new(entry.path()).ok()) - .collect() - } else { - Vec::new() - }; - - // Load frameworks from Frameworks directory - let frameworks_dir = bundle_path.join("Frameworks"); - let frameworks = if frameworks_dir.exists() { - fs::read_dir(&frameworks_dir) - .map_err(|e| { - Error::InvalidBundle(format!("Failed to read Frameworks directory: {}", e)) - })? - .filter_map(|entry| entry.ok()) - .filter(|entry| { - entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false) - && entry.path().join("Info.plist").exists() - }) - .filter_map(|entry| Bundle::new(entry.path()).ok()) - .collect() - } else { - Vec::new() - }; - - // Find all .dylib files in the bundle directory (recursive) - let libraries = find_dylibs(&bundle_path, &bundle_path)?; - - Ok(Bundle { - app_info, - bundle_type: BundleType::from_extension( - bundle_path - .extension() - .and_then(|ext| ext.to_str()) - .unwrap_or(""), - ), - bundle_dir: bundle_path, - app_extensions, - frameworks, - _libraries: libraries, - }) - } - - pub fn set_bundle_identifier(&mut self, id: &str) { - self.app_info.insert( - "CFBundleIdentifier".to_string(), - Value::String(id.to_string()), - ); - } - - pub fn bundle_identifier(&self) -> Option<&str> { - self.app_info - .get("CFBundleIdentifier") - .and_then(|v| v.as_string()) - } - - pub fn bundle_name(&self) -> Option<&str> { - self.app_info - .get("CFBundleName") - .and_then(|v| v.as_string()) - } - - pub fn app_extensions(&self) -> &[Bundle] { - &self.app_extensions - } - - pub fn app_extensions_mut(&mut self) -> &mut [Bundle] { - &mut self.app_extensions - } - - pub fn write_info(&self) -> Result<(), Error> { - let info_plist_path = self.bundle_dir.join("Info.plist"); - let result = plist::to_file_binary(&info_plist_path, &self.app_info); - - if result.is_err() { - return Err(Error::InvalidBundle(format!( - "Failed to write Info.plist: {}", - result.unwrap_err() - ))); - } - Ok(()) - } - - pub fn embedded_bundles(&self) -> Vec<&Bundle> { - let mut bundles = Vec::new(); - bundles.extend(self.app_extensions.iter()); - bundles.extend(self.frameworks.iter()); - bundles.push(self); - bundles.sort_by_key(|b| b.bundle_dir.components().count()); - bundles - } -} - -fn assert_bundle(condition: bool, msg: &str) -> Result<(), Error> { - if !condition { - Err(Error::InvalidBundle(msg.to_string())) - } else { - Ok(()) - } -} - -fn find_dylibs(dir: &Path, bundle_root: &Path) -> Result, Error> { - let mut libraries = Vec::new(); - - fn collect_dylibs( - dir: &Path, - bundle_root: &Path, - libraries: &mut Vec, - ) -> Result<(), Error> { - let entries = fs::read_dir(dir).map_err(|e| { - Error::InvalidBundle(format!("Failed to read directory {}: {}", dir.display(), e)) - })?; - - for entry in entries { - let entry = entry.map_err(|e| { - Error::InvalidBundle(format!("Failed to read directory entry: {}", e)) - })?; - - let path = entry.path(); - let file_type = entry - .file_type() - .map_err(|e| Error::InvalidBundle(format!("Failed to get file type: {}", e)))?; - - if file_type.is_file() { - if let Some(name) = path.file_name().and_then(|n| n.to_str()) - && name.ends_with(".dylib") - { - // Get relative path from bundle root - if let Ok(relative_path) = path.strip_prefix(bundle_root) - && let Some(relative_str) = relative_path.to_str() - { - libraries.push(relative_str.to_string()); - } - } - } else if file_type.is_dir() { - collect_dylibs(&path, bundle_root, libraries)?; - } - } - Ok(()) - } - - collect_dylibs(dir, bundle_root, &mut libraries)?; - Ok(libraries) -} - -// Borrowed from https://github.com/khcrysalis/PlumeImpactor/blob/main/crates/utils/src/bundle.rs -#[derive(Debug, Clone)] -pub enum BundleType { - App, - AppExtension, - Framework, - Unknown, -} - -impl BundleType { - pub fn from_extension(ext: &str) -> Self { - match ext { - "app" => BundleType::App, - "appex" => BundleType::AppExtension, - "framework" => BundleType::Framework, - _ => BundleType::Unknown, - } - } -} diff --git a/isideload/src/certificate.rs b/isideload/src/certificate.rs deleted file mode 100644 index feb445b..0000000 --- a/isideload/src/certificate.rs +++ /dev/null @@ -1,301 +0,0 @@ -// This file was made using https://github.com/Dadoum/Sideloader as a reference. - -use apple_codesign::SigningSettings; -use hex; -use rcgen::{CertificateParams, DnType, KeyPair}; -use rsa::{ - RsaPrivateKey, - pkcs8::{DecodePrivateKey, EncodePrivateKey, LineEnding}, -}; -use sha1::{Digest, Sha1}; -use std::{ - fs, - path::{Path, PathBuf}, -}; -use x509_certificate::{CapturedX509Certificate, InMemorySigningKeyPair, Sign, X509Certificate}; - -use crate::Error; -use crate::developer_session::{DeveloperDeviceType, DeveloperSession, DeveloperTeam}; - -#[derive(Debug)] -pub struct CertificateIdentity { - pub certificate: Option, - pub key_pair: InMemorySigningKeyPair, - pub private_key: RsaPrivateKey, - pub key_file: PathBuf, - pub cert_file: PathBuf, - pub machine_name: String, - pub machine_id: String, -} - -impl CertificateIdentity { - pub async fn new( - configuration_path: &Path, - dev_session: &DeveloperSession, - apple_id: String, - machine_name: String, - ) -> Result { - let mut hasher = Sha1::new(); - hasher.update(apple_id.as_bytes()); - 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(Error::Filesystem)?; - - let key_file = key_path.join("key.pem"); - let cert_file = key_path.join("cert.pem"); - let teams = dev_session.list_teams().await?; - let team = teams - .first() - .ok_or(Error::Certificate("No teams found".to_string()))?; - - let private_key = if key_file.exists() { - let key_data = fs::read_to_string(&key_file) - .map_err(|e| Error::Certificate(format!("Failed to read key file: {}", e)))?; - RsaPrivateKey::from_pkcs8_pem(&key_data) - .map_err(|e| Error::Certificate(format!("Failed to load private key: {}", e)))? - } else { - let mut rng = rand::thread_rng(); - let private_key = RsaPrivateKey::new(&mut rng, 2048) - .map_err(|e| Error::Certificate(format!("Failed to generate RSA key: {}", e)))?; - - let pem_data = private_key - .to_pkcs8_pem(LineEnding::LF) - .map_err(|e| Error::Certificate(format!("Failed to encode private key: {}", e)))?; - fs::write(&key_file, pem_data.as_bytes()).map_err(Error::Filesystem)?; - private_key - }; - - let key_pair = InMemorySigningKeyPair::from_pkcs8_der( - private_key - .to_pkcs8_der() - .map_err(|e| Error::Certificate(format!("Failed to encode private key: {}", e)))? - .as_bytes(), - ) - .map_err(|e| Error::Certificate(format!("Failed to decode private key: {}", e)))?; - - let mut cert_identity = CertificateIdentity { - certificate: None, - key_pair, - private_key, - key_file, - cert_file, - machine_name, - machine_id: "".to_owned(), - }; - - if let Ok((cert, machine_id)) = cert_identity - .find_matching_certificate(dev_session, team) - .await - { - cert_identity.certificate = Some(cert.clone()); - cert_identity.machine_id = machine_id; - - let cert_pem = cert - .encode_pem() - .map_err(|e| Error::Certificate(format!("Failed to encode cert: {}", e)))?; - fs::write(&cert_identity.cert_file, cert_pem).map_err(Error::Filesystem)?; - - return Ok(cert_identity); - } - - cert_identity - .request_new_certificate(dev_session, team) - .await?; - Ok(cert_identity) - } - - async fn find_matching_certificate( - &self, - dev_session: &DeveloperSession, - team: &DeveloperTeam, - ) -> Result<(X509Certificate, String), Error> { - let certificates = dev_session - .list_all_development_certs(DeveloperDeviceType::Ios, team) - .await - .map_err(|e| Error::Certificate(format!("Failed to list certificates: {:?}", e)))?; - - let our_public_key_der = self.key_pair.public_key_data().to_vec(); - - for cert in certificates - .iter() - .filter(|c| c.machine_name == self.machine_name) - { - if let Ok(x509_cert) = X509Certificate::from_der(&cert.cert_content) { - let cert_public_key_der: Vec = x509_cert - .tbs_certificate() - .subject_public_key_info - .subject_public_key - .octets() - .collect(); - if cert_public_key_der == our_public_key_der { - return Ok((x509_cert, cert.machine_id.clone())); - } - } - } - Err(Error::Certificate( - "No matching certificate found".to_string(), - )) - } - - async fn request_new_certificate( - &mut self, - dev_session: &DeveloperSession, - team: &DeveloperTeam, - ) -> Result<(), Error> { - let mut params = CertificateParams::new(vec!["CN".to_string()]) - .map_err(|e| Error::Certificate(format!("Failed to create params: {}", e)))?; - params.distinguished_name.push(DnType::CountryName, "US"); - params - .distinguished_name - .push(DnType::StateOrProvinceName, "STATE"); - params - .distinguished_name - .push(DnType::LocalityName, "LOCAL"); - params - .distinguished_name - .push(DnType::OrganizationName, "ORGNIZATION"); - params.distinguished_name.push(DnType::CommonName, "CN"); - - let key_pem = self - .private_key - .to_pkcs8_pem(LineEnding::LF) - .map_err(|e| Error::Certificate(format!("Failed to encode private key: {}", e)))?; - let key_pair = KeyPair::from_pem(&key_pem) - .map_err(|e| Error::Certificate(format!("Failed to load key pair for CSR: {}", e)))?; - - let csr = params - .serialize_request(&key_pair) - .map_err(|e| Error::Certificate(format!("Failed to generate CSR: {}", e)))?; - let csr_pem = csr - .pem() - .map_err(|e| Error::Certificate(format!("Failed to encode CSR to PEM: {}", e)))?; - - let certificate_id = dev_session - .submit_development_csr( - DeveloperDeviceType::Ios, - team, - csr_pem, - self.machine_name.clone(), - ) - .await - .map_err(|e| { - let is_7460 = match &e { - Error::DeveloperSession(code, _) => *code == 7460, - _ => false, - }; - if is_7460 { - Error::Certificate("You have too many certificates!".to_string()) - } else { - Error::Certificate(format!("Failed to submit CSR: {:?}", e)) - } - })?; - - let certificates = dev_session - .list_all_development_certs(DeveloperDeviceType::Ios, team) - .await?; - - let apple_cert = certificates - .iter() - .find(|cert| cert.certificate_id == certificate_id) - .ok_or(Error::Certificate( - "Certificate not found after submission".to_string(), - ))?; - - let certificate = X509Certificate::from_der(&apple_cert.cert_content) - .map_err(|e| Error::Certificate(format!("Failed to parse certificate: {}", e)))?; - - // Write certificate to disk - let cert_pem = certificate - .encode_pem() - .map_err(|e| Error::Certificate(format!("Failed to encode cert: {}", e)))?; - fs::write(&self.cert_file, cert_pem).map_err(Error::Filesystem)?; - - self.certificate = Some(certificate); - self.machine_id = apple_cert.machine_id.clone(); - - Ok(()) - } - - pub fn get_certificate_file_path(&self) -> &Path { - &self.cert_file - } - - pub fn get_private_key_file_path(&self) -> &Path { - &self.key_file - } - - pub fn get_serial_number(&self) -> Result { - let cert = match &self.certificate { - Some(c) => c, - None => { - return Err(Error::Certificate( - "No certificate available to get serial number".to_string(), - )); - } - }; - - let serial = &cert.tbs_certificate().serial_number; - let hex_str = hex::encode(serial.as_slice()); - - Ok(hex_str.trim_start_matches("0").to_string().to_uppercase()) - } - - pub fn to_pkcs12(&self, password: &str) -> Result, Error> { - let cert = self - .certificate - .as_ref() - .ok_or(Error::Certificate("Certificate not found".to_string()))?; - - let cert_der = cert - .encode_der() - .map_err(|e| Error::Certificate(format!("Failed to encode certificate: {}", e)))?; - - let key_der = self - .private_key - .to_pkcs8_der() - .map_err(|e| Error::Certificate(format!("Failed to encode private key: {}", e)))?; - - let pfx = p12::PFX::new( - &cert_der, - key_der.as_bytes(), - None, - password, - &self.machine_name, - ) - .ok_or(Error::Certificate("Failed to create PKCS#12".to_string()))?; - - Ok(pfx.to_der()) - } - - pub fn to_signing_settings(&self) -> Result, Error> { - let mut settings = SigningSettings::default(); - - let certificate = self - .certificate - .as_ref() - .ok_or(Error::Certificate("Certificate not found".to_string()))?; - - settings.set_signing_key( - &self.key_pair, - CapturedX509Certificate::from_der( - certificate.encode_der().map_err(|e| { - Error::Certificate(format!("Failed to encode certificate: {}", e)) - })?, - ) - .map_err(|e| { - Error::Certificate(format!("Failed to create captured certificate: {}", e)) - })?, - ); - settings.chain_apple_certificates(); - settings.set_for_notarization(false); - settings.set_shallow(true); - settings.set_team_id_from_signing_certificate().ok_or({ - Error::Certificate("Failed to set team ID from signing certificate".to_string()) - })?; - settings - .set_time_stamp_url("http://timestamp.apple.com/ts01") - .map_err(|e| Error::AppleCodesignError(Box::new(e)))?; - - Ok(settings) - } -} diff --git a/isideload/src/developer_session.rs b/isideload/src/developer_session.rs deleted file mode 100644 index 3e70dd4..0000000 --- a/isideload/src/developer_session.rs +++ /dev/null @@ -1,779 +0,0 @@ -// This file was made using https://github.com/Dadoum/Sideloader as a reference for the apple private endpoints - -use crate::Error; -use icloud_auth::{AppleAccount, Error as ICloudError}; -use plist::{Date, Dictionary, Value}; -use serde::{Deserialize, Serialize}; -use std::sync::Arc; -use uuid::Uuid; - -pub struct DeveloperSession { - pub account: Arc, - team: Option, -} - -impl DeveloperSession { - pub fn new(account: Arc) -> Self { - DeveloperSession { - account, - team: None, - } - } - - pub async fn send_developer_request( - &self, - url: &str, - body: Option, - ) -> Result { - let mut request = Dictionary::new(); - request.insert( - "clientId".to_string(), - Value::String("XABBG36SBA".to_string()), - ); - request.insert( - "protocolVersion".to_string(), - Value::String("QH65B2".to_string()), - ); - request.insert( - "requestId".to_string(), - Value::String(Uuid::new_v4().to_string().to_uppercase()), - ); - request.insert( - "userLocale".to_string(), - Value::Array(vec![Value::String("en_US".to_string())]), - ); - if let Some(body) = body { - for (key, value) in body { - request.insert(key, value); - } - } - - let response = self - .account - .send_request(url, Some(request)) - .await - .map_err(|e| { - if let ICloudError::AuthSrpWithMessage(code, message) = e { - Error::DeveloperSession(code, format!("Developer request failed: {}", message)) - } else { - Error::Generic("Failed to send developer request".to_string()) - } - })?; - - let status_code = response - .get("resultCode") - .and_then(|v| v.as_unsigned_integer()) - .unwrap_or(0); - if status_code != 0 { - let description = response - .get("userString") - .and_then(|v| v.as_string()) - .or_else(|| response.get("resultString").and_then(|v| v.as_string())) - .unwrap_or("(null)"); - return Err(Error::DeveloperSession( - status_code as i64, - description.to_string(), - )); - } - Ok(response) - } - - pub async fn list_teams(&self) -> Result, Error> { - let url = "https://developerservices2.apple.com/services/QH65B2/listTeams.action?clientId=XABBG36SBA"; - let response = self.send_developer_request(url, None).await?; - - let teams = response - .get("teams") - .and_then(|v| v.as_array()) - .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("team".to_string()))?; - let name = dict - .get("name") - .and_then(|v| v.as_string()) - .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("teamId".to_string()))? - .to_string(); - result.push(DeveloperTeam { - _name: name, - team_id, - }); - } - Ok(result) - } - - pub async fn get_team(&self) -> Result { - if let Some(team) = &self.team { - return Ok(team.clone()); - } - let teams = self.list_teams().await?; - if teams.is_empty() { - return Err(Error::DeveloperSession( - -1, - "No developer teams found".to_string(), - )); - } - // TODO: Handle multiple teams - Ok(teams[0].clone()) - } - - pub fn set_team(&mut self, team: DeveloperTeam) { - self.team = Some(team); - } - - pub async fn list_devices( - &self, - device_type: DeveloperDeviceType, - team: &DeveloperTeam, - ) -> Result, Error> { - let url = dev_url(device_type, "listDevices"); - let mut body = Dictionary::new(); - body.insert("teamId".to_string(), Value::String(team.team_id.clone())); - let response = self.send_developer_request(&url, Some(body)).await?; - - let devices = response - .get("devices") - .and_then(|v| v.as_array()) - .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("device".to_string()))?; - let device_id = dict - .get("deviceId") - .and_then(|v| v.as_string()) - .ok_or(Error::Parse("deviceId".to_string()))? - .to_string(); - let name = dict - .get("name") - .and_then(|v| v.as_string()) - .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("deviceNumber".to_string()))? - .to_string(); - result.push(DeveloperDevice { - _device_id: device_id, - _name: name, - device_number, - }); - } - Ok(result) - } - - pub async fn add_device( - &self, - device_type: DeveloperDeviceType, - team: &DeveloperTeam, - device_name: &str, - udid: &str, - ) -> Result { - let url = dev_url(device_type, "addDevice"); - let mut body = Dictionary::new(); - body.insert("teamId".to_string(), Value::String(team.team_id.clone())); - body.insert("name".to_string(), Value::String(device_name.to_string())); - body.insert("deviceNumber".to_string(), Value::String(udid.to_string())); - - let response = self.send_developer_request(&url, Some(body)).await?; - - let device_dict = response - .get("device") - .and_then(|v| v.as_dictionary()) - .ok_or(Error::Parse("device".to_string()))?; - - let device_id = device_dict - .get("deviceId") - .and_then(|v| v.as_string()) - .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("name".to_string()))? - .to_string(); - let device_number = device_dict - .get("deviceNumber") - .and_then(|v| v.as_string()) - .ok_or(Error::Parse("deviceNumber".to_string()))? - .to_string(); - - Ok(DeveloperDevice { - _device_id: device_id, - _name: name, - device_number, - }) - } - - pub async fn list_all_development_certs( - &self, - device_type: DeveloperDeviceType, - team: &DeveloperTeam, - ) -> Result, Error> { - let url = dev_url(device_type, "listAllDevelopmentCerts"); - let mut body = Dictionary::new(); - body.insert("teamId".to_string(), Value::String(team.team_id.clone())); - - let response = self.send_developer_request(&url, Some(body)).await?; - - let certs = response - .get("certificates") - .and_then(|v| v.as_array()) - .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("certificate".to_string()))?; - let name = dict - .get("name") - .and_then(|v| v.as_string()) - .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("certificateId".to_string()))? - .to_string(); - let serial_number = dict - .get("serialNumber") - .and_then(|v| v.as_string()) - .ok_or(Error::Parse("serialNumber".to_string()))? - .to_string(); - let machine_name = dict - .get("machineName") - .and_then(|v| v.as_string()) - .unwrap_or("") - .to_string(); - let machine_id = dict - .get("machineId") - .and_then(|v| v.as_string()) - .ok_or(Error::Parse("machineId".to_string()))? - .to_string(); - let cert_content = dict - .get("certContent") - .and_then(|v| v.as_data()) - .ok_or(Error::Parse("certContent".to_string()))? - .to_vec(); - - result.push(DevelopmentCertificate { - name, - certificate_id, - serial_number, - machine_name, - machine_id, - cert_content, - }); - } - Ok(result) - } - - pub async fn revoke_development_cert( - &self, - device_type: DeveloperDeviceType, - team: &DeveloperTeam, - serial_number: &str, - ) -> Result<(), Error> { - let url = dev_url(device_type, "revokeDevelopmentCert"); - let mut body = Dictionary::new(); - body.insert("teamId".to_string(), Value::String(team.team_id.clone())); - body.insert( - "serialNumber".to_string(), - Value::String(serial_number.to_string()), - ); - - self.send_developer_request(&url, Some(body)).await?; - Ok(()) - } - - pub async fn submit_development_csr( - &self, - device_type: DeveloperDeviceType, - team: &DeveloperTeam, - csr_content: String, - machine_name: String, - ) -> Result { - let url = dev_url(device_type, "submitDevelopmentCSR"); - let mut body = Dictionary::new(); - body.insert("teamId".to_string(), Value::String(team.team_id.clone())); - body.insert("csrContent".to_string(), Value::String(csr_content)); - body.insert( - "machineId".to_string(), - Value::String(uuid::Uuid::new_v4().to_string().to_uppercase()), - ); - body.insert("machineName".to_string(), Value::String(machine_name)); - - let response = self.send_developer_request(&url, Some(body)).await?; - let cert_dict = response - .get("certRequest") - .and_then(|v| v.as_dictionary()) - .ok_or(Error::Parse("certRequest".to_string()))?; - let id = cert_dict - .get("certRequestId") - .and_then(|v| v.as_string()) - .ok_or(Error::Parse("certRequestId".to_string()))? - .to_string(); - - Ok(id) - } - - pub async fn list_app_ids( - &self, - device_type: DeveloperDeviceType, - team: &DeveloperTeam, - ) -> Result { - let url = dev_url(device_type, "listAppIds"); - let mut body = Dictionary::new(); - body.insert("teamId".to_string(), Value::String(team.team_id.clone())); - - let response = self.send_developer_request(&url, Some(body)).await?; - - let app_ids = response - .get("appIds") - .and_then(|v| v.as_array()) - .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("appId".to_string()))?; - let name = dict - .get("name") - .and_then(|v| v.as_string()) - .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("appIdId".to_string()))? - .to_string(); - let identifier = dict - .get("identifier") - .and_then(|v| v.as_string()) - .ok_or(Error::Parse("identifier".to_string()))? - .to_string(); - let features = dict - .get("features") - .and_then(|v| v.as_dictionary()) - .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("expirationDate".to_string()))?, - ) - } else { - None - }; - - result.push(AppId { - name, - app_id_id, - identifier, - features: features.clone(), - expiration_date, - }); - } - - let max_quantity = if response.contains_key("maxQuantity") { - Some( - response - .get("maxQuantity") - .and_then(|v| v.as_unsigned_integer()) - .ok_or(Error::Parse("maxQuantity".to_string()))?, - ) - } else { - None - }; - - let available_quantity = if response.contains_key("availableQuantity") { - Some( - response - .get("availableQuantity") - .and_then(|v| v.as_unsigned_integer()) - .ok_or(Error::Parse("availableQuantity".to_string()))?, - ) - } else { - None - }; - - Ok(ListAppIdsResponse { - app_ids: result, - max_quantity, - available_quantity, - }) - } - - pub async fn add_app_id( - &self, - device_type: DeveloperDeviceType, - team: &DeveloperTeam, - name: &str, - identifier: &str, - ) -> Result<(), Error> { - let url = dev_url(device_type, "addAppId"); - let mut body = Dictionary::new(); - body.insert("teamId".to_string(), Value::String(team.team_id.clone())); - body.insert("name".to_string(), Value::String(name.to_string())); - body.insert( - "identifier".to_string(), - Value::String(identifier.to_string()), - ); - - self.send_developer_request(&url, Some(body)).await?; - - Ok(()) - } - - pub async fn update_app_id( - &self, - device_type: DeveloperDeviceType, - team: &DeveloperTeam, - app_id: &AppId, - features: &Dictionary, - ) -> Result { - let url = dev_url(device_type, "updateAppId"); - let mut body = Dictionary::new(); - body.insert( - "appIdId".to_string(), - Value::String(app_id.app_id_id.clone()), - ); - body.insert("teamId".to_string(), Value::String(team.team_id.clone())); - - for (key, value) in features { - body.insert(key.clone(), value.clone()); - } - - let response = self.send_developer_request(&url, Some(body)).await?; - let cert_dict = response - .get("appId") - .and_then(|v| v.as_dictionary()) - .ok_or(Error::Parse("appId".to_string()))?; - let feats = cert_dict - .get("features") - .and_then(|v| v.as_dictionary()) - .ok_or(Error::Parse("features".to_string()))?; - - Ok(feats.clone()) - } - - pub async fn delete_app_id( - &self, - device_type: DeveloperDeviceType, - team: &DeveloperTeam, - app_id_id: String, - ) -> Result<(), Error> { - let url = dev_url(device_type, "deleteAppId"); - let mut body = Dictionary::new(); - body.insert("teamId".to_string(), Value::String(team.team_id.clone())); - body.insert("appIdId".to_string(), Value::String(app_id_id.clone())); - - self.send_developer_request(&url, Some(body)).await?; - - Ok(()) - } - - pub async fn list_application_groups( - &self, - device_type: DeveloperDeviceType, - team: &DeveloperTeam, - ) -> Result, Error> { - let url = dev_url(device_type, "listApplicationGroups"); - let mut body = Dictionary::new(); - body.insert("teamId".to_string(), Value::String(team.team_id.clone())); - - let response = self.send_developer_request(&url, Some(body)).await?; - - let app_groups = response - .get("applicationGroupList") - .and_then(|v| v.as_array()) - .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("applicationGroup".to_string()))?; - let application_group = dict - .get("applicationGroup") - .and_then(|v| v.as_string()) - .ok_or(Error::Parse("applicationGroup".to_string()))? - .to_string(); - let name = dict - .get("name") - .and_then(|v| v.as_string()) - .ok_or(Error::Parse("name".to_string()))? - .to_string(); - let identifier = dict - .get("identifier") - .and_then(|v| v.as_string()) - .ok_or(Error::Parse("identifier".to_string()))? - .to_string(); - - result.push(ApplicationGroup { - application_group, - _name: name, - identifier, - }); - } - - Ok(result) - } - - pub async fn add_application_group( - &self, - device_type: DeveloperDeviceType, - team: &DeveloperTeam, - group_identifier: &str, - name: &str, - ) -> Result { - let url = dev_url(device_type, "addApplicationGroup"); - let mut body = Dictionary::new(); - body.insert("teamId".to_string(), Value::String(team.team_id.clone())); - body.insert("name".to_string(), Value::String(name.to_string())); - body.insert( - "identifier".to_string(), - Value::String(group_identifier.to_string()), - ); - - let response = self.send_developer_request(&url, Some(body)).await?; - let app_group_dict = response - .get("applicationGroup") - .and_then(|v| v.as_dictionary()) - .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("applicationGroup".to_string()))? - .to_string(); - let name = app_group_dict - .get("name") - .and_then(|v| v.as_string()) - .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("identifier".to_string()))? - .to_string(); - - Ok(ApplicationGroup { - application_group, - _name: name, - identifier, - }) - } - - pub async fn assign_application_group_to_app_id( - &self, - device_type: DeveloperDeviceType, - team: &DeveloperTeam, - app_id: &AppId, - app_group: &ApplicationGroup, - ) -> Result<(), Error> { - let url = dev_url(device_type, "assignApplicationGroupToAppId"); - let mut body = Dictionary::new(); - body.insert("teamId".to_string(), Value::String(team.team_id.clone())); - body.insert( - "appIdId".to_string(), - Value::String(app_id.app_id_id.clone()), - ); - body.insert( - "applicationGroups".to_string(), - Value::String(app_group.application_group.clone()), - ); - - self.send_developer_request(&url, Some(body)).await?; - - Ok(()) - } - - pub async fn download_team_provisioning_profile( - &self, - device_type: DeveloperDeviceType, - team: &DeveloperTeam, - app_id: &AppId, - ) -> Result { - let url = dev_url(device_type, "downloadTeamProvisioningProfile"); - let mut body = Dictionary::new(); - body.insert("teamId".to_string(), Value::String(team.team_id.clone())); - body.insert( - "appIdId".to_string(), - Value::String(app_id.app_id_id.clone()), - ); - - let response = self.send_developer_request(&url, Some(body)).await?; - - let profile = response - .get("provisioningProfile") - .and_then(|v| v.as_dictionary()) - .ok_or(Error::Parse("provisioningProfile".to_string()))?; - let name = profile - .get("name") - .and_then(|v| v.as_string()) - .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("provisioningProfileId".to_string()))? - .to_string(); - let encoded_profile = profile - .get("encodedProfile") - .and_then(|v| v.as_data()) - .ok_or(Error::Parse("encodedProfile".to_string()))? - .to_vec(); - - Ok(ProvisioningProfile { - _name: name, - _provisioning_profile_id: provisioning_profile_id, - encoded_profile, - }) - } -} - -#[derive(Debug, Clone)] -pub enum DeveloperDeviceType { - Any, - Ios, - Tvos, - Watchos, -} - -impl DeveloperDeviceType { - pub fn url_segment(&self) -> &'static str { - match self { - DeveloperDeviceType::Any => "", - DeveloperDeviceType::Ios => "ios/", - DeveloperDeviceType::Tvos => "tvos/", - DeveloperDeviceType::Watchos => "watchos/", - } - } -} - -fn dev_url(device_type: DeveloperDeviceType, endpoint: &str) -> String { - format!( - "https://developerservices2.apple.com/services/QH65B2/{}{}.action?clientId=XABBG36SBA", - device_type.url_segment(), - endpoint - ) -} - -#[derive(Debug, Clone)] -pub struct DeveloperDevice { - pub _device_id: String, - pub _name: String, - pub device_number: String, -} - -#[derive(Debug, Clone)] -pub struct DeveloperTeam { - pub _name: String, - pub team_id: String, -} - -#[derive(Debug, Clone)] -pub struct DevelopmentCertificate { - pub name: String, - pub certificate_id: String, - pub serial_number: String, - pub machine_name: String, - pub machine_id: String, - pub cert_content: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct AppId { - pub app_id_id: String, - pub identifier: String, - pub name: String, - pub features: Dictionary, - pub expiration_date: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ListAppIdsResponse { - pub app_ids: Vec, - pub max_quantity: Option, - pub available_quantity: Option, -} - -#[derive(Debug, Clone)] -pub struct ApplicationGroup { - pub application_group: String, - pub _name: String, - pub identifier: String, -} - -#[derive(Debug, Clone)] -pub struct ProvisioningProfile { - pub _provisioning_profile_id: String, - pub _name: String, - pub encoded_profile: Vec, -} - -impl ProvisioningProfile { - // TODO: I'm not sure if this is the proper way to parse this but it works so... - pub fn profile_plist(&self) -> Result { - let start_marker = b""; - - let start = self - .encoded_profile - .windows(start_marker.len()) - .position(|w| w == start_marker) - .ok_or_else(|| { - Error::Generic("Failed to find start of plist in provisioning profile".to_string()) - })?; - - let end = self - .encoded_profile - .windows(end_marker.len()) - .position(|w| w == end_marker) - .ok_or_else(|| { - Error::Generic("Failed to find end of plist in provisioning profile".to_string()) - })? - + end_marker.len(); - - plist::from_bytes::(&self.encoded_profile[start..end]).map_err(|e| { - Error::Generic(format!("Failed to parse provisioning profile plist: {}", e)) - }) - } - - pub fn entitlements_xml(&self) -> Result { - let profile_plist = self.profile_plist()?; - let entitlements = profile_plist.get("Entitlements").ok_or_else(|| { - Error::Generic("No Entitlements found in provisioning profile".to_string()) - })?; - let mut buf = vec![]; - entitlements.to_writer_xml(&mut buf).map_err(|e| { - Error::Generic(format!( - "Failed to convert entitlements to XML for codesigning: {}", - e - )) - })?; - let entitlements = std::str::from_utf8(&buf) - .map_err(|e| { - Error::Generic(format!( - "Failed to convert entitlements to UTF-8 for codesigning: {}", - e - )) - })? - .to_string(); - - Ok(entitlements) - } -} diff --git a/isideload/src/device.rs b/isideload/src/device.rs deleted file mode 100644 index 81a2570..0000000 --- a/isideload/src/device.rs +++ /dev/null @@ -1,91 +0,0 @@ -use idevice::{ - IdeviceService, afc::AfcClient, installation_proxy::InstallationProxyClient, - provider::IdeviceProvider, -}; -use std::pin::Pin; -use std::{future::Future, path::Path}; - -use crate::Error; - -/// Installs an ***already signed*** app onto your device. -/// To sign and install an app, see [`crate::sideload::sideload_app`] -pub async fn install_app( - provider: &impl IdeviceProvider, - app_path: &Path, - progress_callback: impl Fn(u64), -) -> Result<(), Error> { - let mut afc_client = AfcClient::connect(provider) - .await - .map_err(Error::IdeviceError)?; - - let dir = format!( - "PublicStaging/{}", - app_path.file_name().unwrap().to_string_lossy() - ); - afc_upload_dir(&mut afc_client, app_path, &dir).await?; - - let mut instproxy_client = InstallationProxyClient::connect(provider) - .await - .map_err(Error::IdeviceError)?; - - let mut options = plist::Dictionary::new(); - options.insert("PackageType".to_string(), "Developer".into()); - instproxy_client - .install_with_callback( - dir, - Some(plist::Value::Dictionary(options)), - async |(percentage, _)| { - progress_callback(percentage); - }, - (), - ) - .await - .map_err(Error::IdeviceError)?; - - Ok(()) -} - -fn afc_upload_dir<'a>( - afc_client: &'a mut AfcClient, - path: &'a Path, - afc_path: &'a str, -) -> Pin> + Send + 'a>> { - Box::pin(async move { - let entries = std::fs::read_dir(path).map_err(Error::Filesystem)?; - afc_client - .mk_dir(afc_path) - .await - .map_err(Error::IdeviceError)?; - for entry in entries { - let entry = entry.map_err(Error::Filesystem)?; - let path = entry.path(); - if path.is_dir() { - let new_afc_path = format!( - "{}/{}", - afc_path, - path.file_name().unwrap().to_string_lossy() - ); - afc_upload_dir(afc_client, &path, &new_afc_path).await?; - } else { - let mut file_handle = afc_client - .open( - format!( - "{}/{}", - afc_path, - path.file_name().unwrap().to_string_lossy() - ), - idevice::afc::opcode::AfcFopenMode::WrOnly, - ) - .await - .map_err(Error::IdeviceError)?; - let bytes = std::fs::read(&path).map_err(Error::Filesystem)?; - file_handle - .write_entire(&bytes) - .await - .map_err(Error::IdeviceError)?; - file_handle.close().await.map_err(Error::IdeviceError)?; - } - } - Ok(()) - }) -} diff --git a/isideload/src/lib.rs b/isideload/src/lib.rs index 8b843f0..b93cf3f 100644 --- a/isideload/src/lib.rs +++ b/isideload/src/lib.rs @@ -1,123 +1,14 @@ -pub mod application; -pub mod bundle; -pub mod certificate; -pub mod developer_session; -pub mod device; -pub mod sideload; - -use std::io::Error as IOError; - -use apple_codesign::AppleCodesignError; -pub use icloud_auth::{AnisetteConfiguration, AppleAccount}; - -use developer_session::DeveloperTeam; -use idevice::IdeviceError; -use thiserror::Error as ThisError; - -#[derive(Debug, ThisError)] -pub enum Error { - #[error("Authentication error {0}: {1}")] - Auth(i64, String), - #[error("Developer session error {0}: {1}")] - DeveloperSession(i64, String), - #[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(transparent)] - Filesystem(#[from] IOError), - #[error(transparent)] - IdeviceError(#[from] IdeviceError), - #[error(transparent)] - AppleCodesignError(#[from] Box), +pub fn add(left: u64, right: u64) -> u64 { + left + right } -pub trait SideloadLogger: Send + Sync { - fn log(&self, message: &str); - fn error(&self, error: &Error); -} +#[cfg(test)] +mod tests { + use super::*; -pub struct DefaultLogger; - -impl SideloadLogger for DefaultLogger { - fn log(&self, message: &str) { - println!("{message}"); - } - - fn error(&self, error: &Error) { - eprintln!("Error: {}", error); - } -} - -/// Sideload configuration options. -pub struct SideloadConfiguration<'a> { - pub machine_name: String, - pub logger: &'a dyn SideloadLogger, - pub store_dir: std::path::PathBuf, - pub revoke_cert: bool, - pub force_sidestore: bool, - pub skip_register_extensions: bool, -} - -impl Default for SideloadConfiguration<'_> { - fn default() -> Self { - SideloadConfiguration::new() - } -} - -impl<'a> SideloadConfiguration<'a> { - pub fn new() -> Self { - SideloadConfiguration { - machine_name: "isideload".to_string(), - logger: &DefaultLogger, - store_dir: std::env::current_dir().unwrap(), - revoke_cert: false, - force_sidestore: false, - skip_register_extensions: true, - } - } - - /// An arbitrary machine name to appear on the certificate (e.x. "CrossCode") - pub fn set_machine_name(mut self, machine_name: String) -> Self { - self.machine_name = machine_name; - self - } - - /// Logger for reporting progress and errors - pub fn set_logger(mut self, logger: &'a dyn SideloadLogger) -> Self { - self.logger = logger; - self - } - - /// Directory used to store intermediate artifacts (profiles, certs, etc.). This directory will not be cleared at the end. - pub fn set_store_dir(mut self, store_dir: std::path::PathBuf) -> Self { - self.store_dir = store_dir; - self - } - - /// Whether or not to revoke the certificate immediately after installation - #[deprecated( - since = "0.1.0", - note = "Certificates will now be placed in SideStore automatically so there is no need to revoke" - )] - pub fn set_revoke_cert(mut self, revoke_cert: bool) -> Self { - self.revoke_cert = revoke_cert; - self - } - - /// Whether or not to treat the app as SideStore (fixes LiveContainer+SideStore issues) - pub fn set_force_sidestore(mut self, force: bool) -> Self { - self.force_sidestore = force; - self - } - - /// Whether or not to skip registering app extensions (save app IDs, default true) - pub fn set_skip_register_extensions(mut self, skip: bool) -> Self { - self.skip_register_extensions = skip; - self + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); } } diff --git a/isideload/src/sideload.rs b/isideload/src/sideload.rs deleted file mode 100644 index c150208..0000000 --- a/isideload/src/sideload.rs +++ /dev/null @@ -1,463 +0,0 @@ -// This file was made using https://github.com/Dadoum/Sideloader as a reference. - -use apple_codesign::{SettingsScope, UnifiedSigner}; -use idevice::IdeviceService; -use idevice::lockdown::LockdownClient; -use idevice::provider::IdeviceProvider; - -use crate::application::Application; -use crate::developer_session::ProvisioningProfile; -use crate::device::install_app; -use crate::{DeveloperTeam, Error, SideloadConfiguration, SideloadLogger}; -use crate::{ - certificate::CertificateIdentity, - developer_session::{DeveloperDeviceType, DeveloperSession}, -}; -use std::collections::HashMap; -use std::{io::Write, path::PathBuf}; - -fn error_and_return(logger: &dyn SideloadLogger, error: Error) -> Result<(), Error> { - logger.error(&error); - Err(error) -} - -/// Signs and installs an `.ipa` or `.app` onto a device. -/// -/// # Arguments -/// - `device_provider` - [`idevice::provider::IdeviceProvider`] for the device -/// - `dev_session` - Authenticated Apple developer session ([`crate::developer_session::DeveloperSession`]). -/// - `app_path` - Path to the `.ipa` file or `.app` bundle to sign and install -/// - `config` - Sideload configuration options ([`crate::SideloadConfiguration`]) -pub async fn sideload_app( - device_provider: &impl IdeviceProvider, - dev_session: &DeveloperSession, - app_path: PathBuf, - config: SideloadConfiguration<'_>, -) -> Result<(), Error> { - let logger = config.logger; - let mut lockdown_client = match LockdownClient::connect(device_provider).await { - Ok(l) => l, - Err(e) => { - return error_and_return(logger, Error::IdeviceError(e)); - } - }; - - if let Ok(pairing_file) = device_provider.get_pairing_file().await { - lockdown_client - .start_session(&pairing_file) - .await - .map_err(Error::IdeviceError)?; - } - - let device_name = lockdown_client - .get_value(Some("DeviceName"), None) - .await - .map_err(Error::IdeviceError)? - .as_string() - .ok_or(Error::Generic( - "Failed to convert DeviceName to string".to_string(), - ))? - .to_string(); - - let device_uuid = lockdown_client - .get_value(Some("UniqueDeviceID"), None) - .await - .map_err(Error::IdeviceError)? - .as_string() - .ok_or(Error::Generic( - "Failed to convert UniqueDeviceID to string".to_string(), - ))? - .to_string(); - - let team = match dev_session.get_team().await { - Ok(t) => t, - Err(e) => { - return error_and_return(logger, e); - } - }; - - logger.log("Successfully retrieved team"); - - ensure_device_registered(logger, dev_session, &team, &device_uuid, &device_name).await?; - - let cert = match CertificateIdentity::new( - &config.store_dir, - dev_session, - dev_session.account.apple_id.clone(), - config.machine_name, - ) - .await - { - Ok(c) => c, - Err(e) => { - return error_and_return(logger, e); - } - }; - - 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 error_and_return(logger, e); - } - }; - - let mut app = Application::new(app_path)?; - let is_sidestore = config.force_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 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 error_and_return( - logger, - Error::InvalidBundle("No bundle name found in IPA".to_string()), - ); - } - }; - - let extensions = app.bundle.app_extensions_mut(); - // for each extension, ensure it has a unique bundle identifier that starts with the main app's bundle identifier - 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 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!( - "{}{}", - main_app_id_str, - &id[main_app_bundle_id.len()..] - )); - } - } - } - app.bundle.set_bundle_identifier(&main_app_id_str); - - let extension_refs: Vec<_> = app.bundle.app_extensions().iter().collect(); - let mut bundles_with_app_id = vec![&app.bundle]; - bundles_with_app_id.extend(extension_refs); - - let app_ids_to_register = bundles_with_app_id - .iter() - .filter(|bundle| { - let bundle_id = bundle.bundle_identifier().unwrap_or(""); - !list_app_id_response - .app_ids - .iter() - .any(|app_id| app_id.identifier == bundle_id) - }) - .collect::>(); - - if let Some(available) = list_app_id_response.available_quantity - && app_ids_to_register.len() > available.try_into().unwrap() - { - return error_and_return( - logger, - Error::InvalidBundle(format!( - "This app requires {} app ids, but you only have {} available", - app_ids_to_register.len(), - available - )), - ); - } - - for bundle in app_ids_to_register { - let id = bundle.bundle_identifier().unwrap_or(""); - let name = bundle.bundle_name().unwrap_or(""); - if let Err(e) = dev_session - .add_app_id(DeveloperDeviceType::Ios, &team, name, id) - .await - { - return error_and_return(logger, e); - } - } - list_app_id_response = match dev_session - .list_app_ids(DeveloperDeviceType::Ios, &team) - .await - { - Ok(ids) => ids, - Err(e) => { - return error_and_return(logger, e); - } - }; - - let mut app_ids: Vec<_> = list_app_id_response - .app_ids - .into_iter() - .filter(|app_id| { - bundles_with_app_id - .iter() - .any(|bundle| app_id.identifier == bundle.bundle_identifier().unwrap_or("")) - }) - .collect(); - let main_app_id = match app_ids - .iter() - .find(|app_id| app_id.identifier == main_app_id_str) - .cloned() - { - Some(id) => id, - None => { - return error_and_return( - logger, - Error::Generic(format!( - "Main app ID {} not found in registered app IDs", - main_app_id_str - )), - ); - } - }; - - logger.log("Successfully registered app IDs"); - - for app_id in app_ids.iter_mut() { - let app_group_feature_enabled = app_id - .features - .get( - "APG3427HIY", /* Gotta love apple and their magic strings! */ - ) - .and_then(|v| v.as_boolean()) - .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)); - let new_features = match dev_session - .update_app_id(DeveloperDeviceType::Ios, &team, app_id, &body) - .await - { - Ok(new_feats) => new_feats, - Err(e) => { - return error_and_return(logger, e); - } - }; - app_id.features = new_features; - } - } - - let group_identifier = format!( - "group.{}", - if config.force_sidestore { - format!("com.SideStore.SideStore.{}", team.team_id) - } else { - main_app_id_str.clone() - } - ); - - if is_sidestore { - app.bundle.app_info.insert( - "ALTAppGroups".to_string(), - plist::Value::Array(vec![plist::Value::String(group_identifier.clone())]), - ); - - app.bundle.app_info.insert( - "ALTCertificateID".to_string(), - plist::Value::String(cert.get_serial_number().unwrap()), - ); - - match cert.to_pkcs12(&cert.machine_id) { - Ok(p12_bytes) => { - let alt_cert_path = app.bundle.bundle_dir.join("ALTCertificate.p12"); - if alt_cert_path.exists() { - std::fs::remove_file(&alt_cert_path).map_err(Error::Filesystem)?; - } - - let mut file = std::fs::File::create(&alt_cert_path).map_err(Error::Filesystem)?; - file.write_all(&p12_bytes).map_err(Error::Filesystem)?; - } - Err(e) => return error_and_return(logger, e), - } - } - - let app_groups = match dev_session - .list_application_groups(DeveloperDeviceType::Ios, &team) - .await - { - Ok(groups) => groups, - Err(e) => { - return error_and_return(logger, e); - } - }; - - let matching_app_groups = app_groups - .iter() - .filter(|group| group.identifier == group_identifier.clone()) - .collect::>(); - - let app_group = if matching_app_groups.is_empty() { - match dev_session - .add_application_group( - DeveloperDeviceType::Ios, - &team, - &group_identifier, - &main_app_name, - ) - .await - { - Ok(group) => group, - Err(e) => { - return error_and_return(logger, e); - } - } - } else { - matching_app_groups[0].clone() - }; - - let mut provisioning_profiles: HashMap = HashMap::new(); - for app_id in app_ids { - let assign_res = dev_session - .assign_application_group_to_app_id( - DeveloperDeviceType::Ios, - &team, - &app_id, - &app_group, - ) - .await; - if assign_res.is_err() { - return error_and_return(logger, assign_res.err().unwrap()); - } - let provisioning_profile = match dev_session - .download_team_provisioning_profile(DeveloperDeviceType::Ios, &team, &app_id) - .await - { - Ok(pp /* tee hee */) => pp, - Err(e) => { - return error_and_return(logger, e); - } - }; - provisioning_profiles.insert(app_id.identifier.clone(), provisioning_profile); - } - - logger.log("Successfully registered app groups"); - - let profile_path = app.bundle.bundle_dir.join("embedded.mobileprovision"); - - if profile_path.exists() { - std::fs::remove_file(&profile_path).map_err(Error::Filesystem)?; - } - - let mut file = std::fs::File::create(&profile_path).map_err(Error::Filesystem)?; - file.write_all(&provisioning_profile.encoded_profile) - .map_err(Error::Filesystem)?; - - // Without this, zsign complains it can't find the provision file - #[cfg(target_os = "windows")] - { - file.sync_all().map_err(|e| Error::Filesystem(e))?; - drop(file); - } - - app.bundle.write_info()?; - for ext in app.bundle.app_extensions_mut() { - ext.write_info()?; - } - - // Collect owned bundle identifiers and directories so we don't capture `app` or `logger` by reference in the blocking thread. - let embedded_bundles_info: Vec<(String, PathBuf)> = app - .bundle - .embedded_bundles() - .iter() - .map(|bundle| { - ( - bundle.bundle_identifier().unwrap_or("Unknown").to_string(), - bundle.bundle_dir.clone(), - ) - }) - .collect(); - let main_bundle_dir = app.bundle.bundle_dir.clone(); - - // Log bundle signing messages outside the blocking closure to avoid capturing non-'static references. - for (id, _) in &embedded_bundles_info { - logger.log(&format!("Signing bundle: {}", id)); - } - - // Move owned data (cert, provisioning_profile, embedded_bundles_info) into the blocking task. - tokio::task::spawn_blocking(move || { - for (_id, bundle_dir) in embedded_bundles_info { - // Recreate settings for each bundle so ownership is clear and we don't move settings across iterations. - let mut settings = cert.to_signing_settings()?; - settings - .set_entitlements_xml( - SettingsScope::Main, - provisioning_profile.entitlements_xml()?, - ) - .map_err(|e| Error::AppleCodesignError(Box::new(e)))?; - - let signer = UnifiedSigner::new(settings); - - signer - .sign_path_in_place(&bundle_dir) - .map_err(|e| Error::AppleCodesignError(Box::new(e)))?; - } - Ok::<(), Error>(()) - }) - .await - .map_err(|e| Error::Generic(format!("Signing task failed: {}", e)))??; - - logger.log("Sucessfully signed app"); - - logger.log("Installing app... 0%"); - - let res = install_app(device_provider, &main_bundle_dir, |percentage| { - logger.log(&format!("Installing app... {}%", percentage)); - }) - .await; - if let Err(e) = res { - return error_and_return(logger, e); - } - - // if config.revoke_cert { - // dev_session - // .revoke_development_cert(DeveloperDeviceType::Ios, &team, &cert.get_serial_number()?) - // .await?; - // logger.log("Certificate revoked"); - // } - - Ok(()) -} - -pub async fn ensure_device_registered( - logger: &dyn SideloadLogger, - dev_session: &DeveloperSession, - team: &DeveloperTeam, - uuid: &str, - name: &str, -) -> Result<(), Error> { - let devices = dev_session - .list_devices(DeveloperDeviceType::Ios, team) - .await; - if let Err(e) = devices { - return error_and_return(logger, e); - } - let devices = devices.unwrap(); - if !devices.iter().any(|d| d.device_number == uuid) { - logger.log("Device not found in your account"); - // TODO: Actually test! - dev_session - .add_device(DeveloperDeviceType::Ios, team, name, uuid) - .await?; - logger.log("Successfully added device to your account"); - } - logger.log("Device is a development device"); - Ok(()) -} From 01d3872181289d82cba878f91bf54e1540115c46 Mon Sep 17 00:00:00 2001 From: nab138 Date: Tue, 16 Dec 2025 20:35:44 -0500 Subject: [PATCH 04/71] add idevice and plist deps --- Cargo.lock | 567 +++++++++++++++++++++++++++++++++++++++++++ isideload/Cargo.toml | 2 + isideload/src/lib.rs | 16 +- 3 files changed, 573 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dafa1e3..fd3d513 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,573 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aws-lc-rs" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88aab2464f1f25453baa7a07c84c5b7684e274054ba06817f382357f77a288" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45afffdee1e7c9126814751f88dddc747f41d91da16c9551a0f1e8a11e788a1" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "cc" +version = "1.2.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cmake" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b042e5d8a74ae91bb0961acd039822472ec99f8ab0948cbf6d1369588f8be586" +dependencies = [ + "cc", +] + +[[package]] +name = "deranged" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "idevice" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "effc7c8fe1bfa5051717c4dc20c3bcc4828b13009a6c95ff6fc8c3f8ccdf4d43" +dependencies = [ + "base64", + "plist", + "rustls", + "serde", + "thiserror", + "tokio", + "tokio-rustls", + "tracing", +] + +[[package]] +name = "indexmap" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "isideload" version = "0.2.0" +dependencies = [ + "idevice", + "plist", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "libc" +version = "0.2.178" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "plist" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" +dependencies = [ + "base64", + "indexmap", + "quick-xml", + "serde", + "time", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys", +] + +[[package]] +name = "rustls" +version = "0.23.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +dependencies = [ + "aws-lc-rs", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "bytes", + "pin-project-lite", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tracing" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index b3f0f0d..ba63929 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -14,3 +14,5 @@ readme = "../README.md" default = [] [dependencies] +idevice = "0.1.50" +plist = "1.8.0" diff --git a/isideload/src/lib.rs b/isideload/src/lib.rs index b93cf3f..4751e79 100644 --- a/isideload/src/lib.rs +++ b/isideload/src/lib.rs @@ -1,14 +1,6 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right -} +use idevice::plist; +use plist::Value; -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } +pub fn test() -> Value { + plist!("value") } From 4f2fd8c8c2acacf22af397bc6e84cb875f2fdaaa Mon Sep 17 00:00:00 2001 From: nab138 Date: Wed, 17 Dec 2025 12:13:40 -0500 Subject: [PATCH 05/71] Add example project --- Cargo.lock | 7 +++++++ Cargo.toml | 4 ++-- examples/minimal/.gitignore | 1 + examples/minimal/Cargo.toml | 7 +++++++ examples/minimal/src/main.rs | 3 +++ isideload/src/auth/mod.rs | 0 isideload/src/lib.rs | 14 ++++++++++---- 7 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 examples/minimal/.gitignore create mode 100644 examples/minimal/Cargo.toml create mode 100644 examples/minimal/src/main.rs create mode 100644 isideload/src/auth/mod.rs diff --git a/Cargo.lock b/Cargo.lock index fd3d513..1a12c78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -187,6 +187,13 @@ version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +[[package]] +name = "minimal" +version = "0.1.0" +dependencies = [ + "isideload", +] + [[package]] name = "num-conv" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 08a856b..b546fb1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,4 @@ [workspace] resolver = "2" -members = ["isideload"] -default-members = ["isideload"] \ No newline at end of file +members = ["examples/minimal","isideload"] +default-members = ["isideload"] diff --git a/examples/minimal/.gitignore b/examples/minimal/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/examples/minimal/.gitignore @@ -0,0 +1 @@ +/target diff --git a/examples/minimal/Cargo.toml b/examples/minimal/Cargo.toml new file mode 100644 index 0000000..45d4403 --- /dev/null +++ b/examples/minimal/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "minimal" +version = "0.1.0" +edition = "2024" + +[dependencies] +isideload = { path = "../../isideload" } \ No newline at end of file diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs new file mode 100644 index 0000000..f6db85d --- /dev/null +++ b/examples/minimal/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + isideload::test(); +} diff --git a/isideload/src/auth/mod.rs b/isideload/src/auth/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/isideload/src/lib.rs b/isideload/src/lib.rs index 4751e79..c037e13 100644 --- a/isideload/src/lib.rs +++ b/isideload/src/lib.rs @@ -1,6 +1,12 @@ -use idevice::plist; -use plist::Value; +pub mod auth; -pub fn test() -> Value { - plist!("value") +use idevice::{plist, pretty_print_plist}; + +pub fn test() -> () { + println!( + "{}", + pretty_print_plist(&plist!({ + "code": "hello" + })) + ); } From d88c8a19fd509f86267f95e1d0f51043f92f5b8f Mon Sep 17 00:00:00 2001 From: nab138 Date: Tue, 6 Jan 2026 09:32:26 -0500 Subject: [PATCH 06/71] Start scaffolding apple account --- Cargo.lock | 1437 ++++++++++++++++++++++++++- isideload/Cargo.toml | 7 +- isideload/src/anisette/mod.rs | 0 isideload/src/auth/apple_account.rs | 27 + isideload/src/auth/apple_root.der | Bin 0 -> 1215 bytes isideload/src/auth/mod.rs | 1 + isideload/src/lib.rs | 12 +- 7 files changed, 1460 insertions(+), 24 deletions(-) create mode 100644 isideload/src/anisette/mod.rs create mode 100644 isideload/src/auth/apple_account.rs create mode 100644 isideload/src/auth/apple_root.der diff --git a/Cargo.lock b/Cargo.lock index 1a12c78..0453024 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,31 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "async-compression" +version = "0.4.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ec5f6c2f8bc326c994cb9e241cc257ddaba9afa8555a43cffbb5dd86efaa37" +dependencies = [ + "compression-codecs", + "compression-core", + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "aws-lc-rs" version = "1.15.2" @@ -30,6 +55,18 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + [[package]] name = "bytes" version = "1.11.0" @@ -48,12 +85,24 @@ dependencies = [ "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "cmake" version = "0.1.56" @@ -63,6 +112,68 @@ dependencies = [ "cc", ] +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "compression-codecs" +version = "0.4.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0f7ac3e5b97fdce45e8922fb05cae2c37f7bbd63d30dd94821dacfd8f3f2bf2" +dependencies = [ + "compression-core", + "flate2", + "memchr", +] + +[[package]] +name = "compression-core" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "deranged" version = "0.5.5" @@ -72,12 +183,32 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "dunce" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -90,12 +221,76 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +[[package]] +name = "flate2" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + [[package]] name = "fs_extra" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -103,8 +298,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -114,9 +311,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "h2" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", ] [[package]] @@ -125,6 +343,190 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + [[package]] name = "idevice" version = "0.1.50" @@ -135,12 +537,33 @@ dependencies = [ "plist", "rustls", "serde", - "thiserror", + "thiserror 2.0.17", "tokio", "tokio-rustls", "tracing", ] +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indexmap" version = "2.12.1" @@ -151,12 +574,30 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "isideload" version = "0.2.0" dependencies = [ "idevice", "plist", + "plist-macro", + "reqwest", ] [[package]] @@ -165,6 +606,28 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "jobserver" version = "0.1.34" @@ -175,18 +638,52 @@ dependencies = [ "libc", ] +[[package]] +name = "js-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "libc" version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "memchr" version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "minimal" version = "0.1.0" @@ -194,6 +691,27 @@ dependencies = [ "isideload", ] +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -206,12 +724,30 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "openssl-probe" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "plist" version = "1.8.0" @@ -225,12 +761,39 @@ dependencies = [ "time", ] +[[package]] +name = "plist-macro" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb72007326fe20721ef27304fcf2d1bd5877b92d13dbd8df735fd33407e31c2a" +dependencies = [ + "plist", +] + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.103" @@ -249,6 +812,62 @@ dependencies = [ "memchr", ] +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.17", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.17", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "quote" version = "1.0.42" @@ -264,6 +883,75 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "reqwest" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e9018c9d814e5f30cc16a0f03271aeab3571e609612d9fe78c1aa8d11c2f62" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "ring" version = "0.17.14" @@ -275,9 +963,15 @@ dependencies = [ "getrandom 0.2.16", "libc", "untrusted", - "windows-sys", + "windows-sys 0.52.0", ] +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustls" version = "0.23.35" @@ -292,15 +986,55 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pki-types" version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" dependencies = [ + "web-time", "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.103.8" @@ -313,6 +1047,59 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "security-framework" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +dependencies = [ + "bitflags", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.228" @@ -343,12 +1130,59 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "subtle" version = "2.6.1" @@ -366,13 +1200,74 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[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.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.17", +] + +[[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", ] [[package]] @@ -417,6 +1312,31 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.48.0" @@ -424,7 +1344,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ "bytes", + "libc", + "mio", "pin-project-lite", + "socket2", + "windows-sys 0.61.2", ] [[package]] @@ -437,6 +1361,69 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "async-compression", + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "iri-string", + "pin-project-lite", + "tokio", + "tokio-util", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" version = "0.1.43" @@ -468,6 +1455,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "unicode-ident" version = "1.0.22" @@ -480,6 +1473,43 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -495,13 +1525,186 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -510,72 +1713,282 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "wit-bindgen" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index ba63929..e5fddb5 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -11,8 +11,11 @@ keywords = ["ios", "sideload"] readme = "../README.md" [features] -default = [] +default = ["install"] +install = ["dep:idevice"] [dependencies] -idevice = "0.1.50" +idevice = { version = "0.1.50", optional = true } plist = "1.8.0" +plist-macro = "0.1.0" +reqwest = { version = "0.13.1", features = ["json", "gzip"] } diff --git a/isideload/src/anisette/mod.rs b/isideload/src/anisette/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/isideload/src/auth/apple_account.rs b/isideload/src/auth/apple_account.rs new file mode 100644 index 0000000..6f711f4 --- /dev/null +++ b/isideload/src/auth/apple_account.rs @@ -0,0 +1,27 @@ +use reqwest::{Certificate, ClientBuilder}; + +const APPLE_ROOT: &[u8] = include_bytes!("./apple_root.der"); + +pub struct AppleAccount { + pub email: String, + pub spd: Option, + pub client: reqwest::Client, +} + +impl AppleAccount { + pub fn new(email: &str) -> reqwest::Result { + let client = ClientBuilder::new() + .add_root_certificate(Certificate::from_der(APPLE_ROOT)?) + // uncomment when debugging w/ charles proxy + // .danger_accept_invalid_certs(true) + .http1_title_case_headers() + .connection_verbose(true) + .build()?; + + Ok(AppleAccount { + email: email.to_string(), + spd: None, + client, + }) + } +} diff --git a/isideload/src/auth/apple_root.der b/isideload/src/auth/apple_root.der new file mode 100644 index 0000000000000000000000000000000000000000..8a9ff247419dd22a07c837ba7394aeabdd0f14e2 GIT binary patch literal 1215 zcmXqLV%crb#JqR`GZP~d5E<~YacZ@Bw0-AgWMpM!Fi0}wHsEAq4rO5zW(o~96gCh9 zakzxJ9199^QWZS&lJyML3{*gZ+`_UDLFd$>lFYQsBg2!4D>>yS-j;I@c+L7YuChh5U7`KHvAiEsOqE5wMI&W5Px=0B&b;#hyADPKr1x`dQTTp(jgCTo z!8UtFgP!fq=lSQ_e%AKXkUH`2+}53ZH{)ckownU-we|}?AHyW>jf!G=C0A{DZzqYZ zUR*fIJvj8>dVR;uKYl+hIQwj|k87R0Pjj_t->jT;Rj-bAq&^<-@B zm%W!-{69S|b&uzbviZg$sSC@eoYZAvW@KPo+{9P~43RPeK43h`@-s62XJG-R8#V)e z5MLO?XEk63QUIB4HrbfL%co zBPgB8DzG#$asX{)0b&Md!c0zKWi)8~WT3^yq0I(NqwGwKVsaTJB?ZM+`ugSN<$8&r zl&P1TpQ{gMB`4||G#-X4W-@5pCe^q(C^aWDF)uk)0hmHdGBS%5lHrLqRUxTTAu+E~ zp&+rS1js5bF3n9XR!B@vPAw>b=t%?WNd@6N1&|%Uq@D!K48=g%l*FPGg_6{wT%d-$ z6ouscyp&8(HYirePg5u@PSruNs30Gx7i1YwCER{crYR^&OfJa;IuB@ONosCtUP-YY za{2^jO7!e*{cX?eJDxY@8r; zW8atJ+3zl;@Sm>qH@UIM?q|jS>=W#7YAu_)gB31Y9ND;kmOoeaf9*e!%UL;V#2vx} zUUwk!R<>!CBEM2a=7;zgw~E zguTASugG_6SFxo3)|+Pa2irq$E}yy6$m#cutA+FG76xsX-aFYzMMzw9>OIdRD+ Xyc@&=R&`yy_2kb5PImJRrKO4hMS!&; literal 0 HcmV?d00001 diff --git a/isideload/src/auth/mod.rs b/isideload/src/auth/mod.rs index e69de29..7af46c6 100644 --- a/isideload/src/auth/mod.rs +++ b/isideload/src/auth/mod.rs @@ -0,0 +1 @@ +pub mod apple_account; diff --git a/isideload/src/lib.rs b/isideload/src/lib.rs index c037e13..2aa4350 100644 --- a/isideload/src/lib.rs +++ b/isideload/src/lib.rs @@ -1,12 +1,4 @@ +pub mod anisette; pub mod auth; -use idevice::{plist, pretty_print_plist}; - -pub fn test() -> () { - println!( - "{}", - pretty_print_plist(&plist!({ - "code": "hello" - })) - ); -} +pub fn test() -> () {} From d97e4b95d0cc9b6af91e3b2316c0a4e3b9908b64 Mon Sep 17 00:00:00 2001 From: nab138 Date: Wed, 7 Jan 2026 11:26:14 -0500 Subject: [PATCH 07/71] Add error struct --- Cargo.lock | 1 + isideload/Cargo.toml | 1 + isideload/src/anisette/mod.rs | 6 +++++ isideload/src/auth/apple_account.rs | 35 ++++++++++++++++++++--------- isideload/src/lib.rs | 10 +++++++++ 5 files changed, 43 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0453024..89b9fdb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -598,6 +598,7 @@ dependencies = [ "plist", "plist-macro", "reqwest", + "thiserror 2.0.17", ] [[package]] diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index e5fddb5..0cf797a 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -19,3 +19,4 @@ idevice = { version = "0.1.50", optional = true } plist = "1.8.0" plist-macro = "0.1.0" reqwest = { version = "0.13.1", features = ["json", "gzip"] } +thiserror = "2.0.17" diff --git a/isideload/src/anisette/mod.rs b/isideload/src/anisette/mod.rs index e69de29..d3d07a6 100644 --- a/isideload/src/anisette/mod.rs +++ b/isideload/src/anisette/mod.rs @@ -0,0 +1,6 @@ +pub trait AnisetteProvider {} + +// tmp +pub struct DefaultAnisetteProvider {} + +impl AnisetteProvider for DefaultAnisetteProvider {} diff --git a/isideload/src/auth/apple_account.rs b/isideload/src/auth/apple_account.rs index 6f711f4..03a4904 100644 --- a/isideload/src/auth/apple_account.rs +++ b/isideload/src/auth/apple_account.rs @@ -1,3 +1,4 @@ +use crate::Result; use reqwest::{Certificate, ClientBuilder}; const APPLE_ROOT: &[u8] = include_bytes!("./apple_root.der"); @@ -6,22 +7,36 @@ pub struct AppleAccount { pub email: String, pub spd: Option, pub client: reqwest::Client, + pub anisette: Box, } impl AppleAccount { - pub fn new(email: &str) -> reqwest::Result { - let client = ClientBuilder::new() - .add_root_certificate(Certificate::from_der(APPLE_ROOT)?) - // uncomment when debugging w/ charles proxy - // .danger_accept_invalid_certs(true) - .http1_title_case_headers() - .connection_verbose(true) - .build()?; - + /// Create a new AppleAccount with the given email + /// + /// # Arguments + /// - `email`: The Apple ID email address + pub fn new(email: &str) -> Result { Ok(AppleAccount { email: email.to_string(), spd: None, - client, + client: Self::build_client(false)?, + anisette: Box::new(crate::anisette::DefaultAnisetteProvider {}), }) } + + /// Build a reqwest client with the Apple root certificate + /// + /// # Arguments + /// - `debug`: DANGER, If true, accept invalid certificates and enable verbose connection logging + pub fn build_client(debug: bool) -> Result { + let cert = Certificate::from_der(APPLE_ROOT)?; + let client = ClientBuilder::new() + .add_root_certificate(cert) + .http1_title_case_headers() + .danger_accept_invalid_certs(debug) + .connection_verbose(debug) + .build()?; + + Ok(client) + } } diff --git a/isideload/src/lib.rs b/isideload/src/lib.rs index 2aa4350..0bf7265 100644 --- a/isideload/src/lib.rs +++ b/isideload/src/lib.rs @@ -1,4 +1,14 @@ +use thiserror::Error as ThisError; + pub mod anisette; pub mod auth; +#[derive(Debug, ThisError)] +pub enum Error { + #[error("Reqwest error: {0}")] + Reqwest(#[from] reqwest::Error), +} + +pub type Result = std::result::Result; + pub fn test() -> () {} From f4687ac3be680d6540b241fdf8d0f8519164af71 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 17 Jan 2026 09:25:00 -0500 Subject: [PATCH 08/71] Keep scaffolding apple account --- Cargo.lock | 10 ++--- examples/minimal/src/main.rs | 19 +++++++++- isideload/Cargo.toml | 2 +- isideload/src/auth/apple_account.rs | 57 +++++++++++++++++++++++++++-- isideload/src/lib.rs | 2 - 5 files changed, 77 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 89b9fdb..a8b294f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -529,9 +529,9 @@ dependencies = [ [[package]] name = "idevice" -version = "0.1.50" +version = "0.1.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "effc7c8fe1bfa5051717c4dc20c3bcc4828b13009a6c95ff6fc8c3f8ccdf4d43" +checksum = "d99be8d1969168237987908274fe41bcb2c8a1f9a37aa12fd15d5713acb9dd2b" dependencies = [ "base64", "plist", @@ -866,7 +866,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -1027,7 +1027,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -1619,7 +1619,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index f6db85d..b9dadba 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -1,3 +1,20 @@ +use std::{env, path::PathBuf}; + +use isideload::auth::apple_account::AppleAccountBuilder; + fn main() { - isideload::test(); + let args: Vec = env::args().collect(); + let app_path = PathBuf::from( + args.get(1) + .expect("Please provide the path to the app to install"), + ); + let apple_id = args + .get(2) + .expect("Please provide the Apple ID to use for installation"); + let apple_password = args.get(3).expect("Please provide the Apple ID password"); + + let account = AppleAccountBuilder::new(apple_id) + .danger_debug(true) + .build() + .unwrap(); } diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 0cf797a..2ed76ae 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -15,7 +15,7 @@ default = ["install"] install = ["dep:idevice"] [dependencies] -idevice = { version = "0.1.50", optional = true } +idevice = { version = "0.1.51", optional = true } plist = "1.8.0" plist-macro = "0.1.0" reqwest = { version = "0.13.1", features = ["json", "gzip"] } diff --git a/isideload/src/auth/apple_account.rs b/isideload/src/auth/apple_account.rs index 03a4904..af88953 100644 --- a/isideload/src/auth/apple_account.rs +++ b/isideload/src/auth/apple_account.rs @@ -10,16 +10,63 @@ pub struct AppleAccount { pub anisette: Box, } -impl AppleAccount { - /// Create a new AppleAccount with the given email +#[derive(Debug)] +pub struct AppleAccountBuilder { + email: String, + debug: Option, +} + +impl AppleAccountBuilder { + /// Create a new AppleAccountBuilder with the given email /// /// # Arguments /// - `email`: The Apple ID email address - pub fn new(email: &str) -> Result { + pub fn new(email: &str) -> Self { + Self { + email: email.to_string(), + debug: None, + } + } + + /// DANGER Set whether to enable debug mode + /// + /// # Arguments + /// - `debug`: If true, accept invalid certificates and enable verbose connection logging + pub fn danger_debug(mut self, debug: bool) -> Self { + self.debug = Some(debug); + self + } + + /// Build the AppleAccount + /// + /// # Errors + /// Returns an error if the reqwest client cannot be built + pub fn login(self) -> Result { + let debug = self.debug.unwrap_or(false); + + AppleAccount::login(&self.email, debug) + } +} + +impl AppleAccount { + /// Create a new AppleAccountBuilder with the given email + /// + /// # Arguments + /// - `email`: The Apple ID email address + pub fn builder(email: &str) -> AppleAccountBuilder { + AppleAccountBuilder::new(email) + } + + /// Log in to an Apple account with the given email + /// + /// Reccomended to use the AppleAccountBuilder instead + pub fn login(email: &str, debug: bool) -> Result { + let client = Self::build_client(debug)?; + Ok(AppleAccount { email: email.to_string(), spd: None, - client: Self::build_client(false)?, + client, anisette: Box::new(crate::anisette::DefaultAnisetteProvider {}), }) } @@ -28,6 +75,8 @@ impl AppleAccount { /// /// # Arguments /// - `debug`: DANGER, If true, accept invalid certificates and enable verbose connection logging + /// # Errors + /// Returns an error if the reqwest client cannot be built pub fn build_client(debug: bool) -> Result { let cert = Certificate::from_der(APPLE_ROOT)?; let client = ClientBuilder::new() diff --git a/isideload/src/lib.rs b/isideload/src/lib.rs index 0bf7265..c5b0051 100644 --- a/isideload/src/lib.rs +++ b/isideload/src/lib.rs @@ -10,5 +10,3 @@ pub enum Error { } pub type Result = std::result::Result; - -pub fn test() -> () {} From 4e50c5c1d4feb0a8e393c48bf93fe64070c0da8d Mon Sep 17 00:00:00 2001 From: nab138 Date: Thu, 22 Jan 2026 12:34:34 -0500 Subject: [PATCH 09/71] Keep working on anisette --- Cargo.lock | 297 +++++++++++++++++++++++++++- README.md | 100 +--------- examples/minimal/Cargo.toml | 4 +- examples/minimal/src/main.rs | 24 ++- isideload/Cargo.toml | 3 + isideload/src/anisette/mod.rs | 7 +- isideload/src/anisette/remote_v3.rs | 92 +++++++++ isideload/src/auth/apple_account.rs | 50 ++++- isideload/src/auth/grandslam.rs | 64 ++++++ isideload/src/auth/mod.rs | 1 + isideload/src/lib.rs | 15 +- 11 files changed, 534 insertions(+), 123 deletions(-) create mode 100644 isideload/src/anisette/remote_v3.rs create mode 100644 isideload/src/auth/grandslam.rs diff --git a/Cargo.lock b/Cargo.lock index a8b294f..b83065f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,74 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + [[package]] name = "async-compression" version = "0.4.36" @@ -27,6 +95,12 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + [[package]] name = "aws-lc-rs" version = "1.15.2" @@ -103,6 +177,19 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chrono" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + [[package]] name = "cmake" version = "0.1.56" @@ -112,6 +199,12 @@ dependencies = [ "cc", ] +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + [[package]] name = "combine" version = "4.6.7" @@ -209,6 +302,29 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_filter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -446,6 +562,30 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "icu_collections" version = "2.1.1" @@ -590,15 +730,24 @@ dependencies = [ "serde", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "isideload" version = "0.2.0" dependencies = [ + "chrono", "idevice", + "log", "plist", "plist-macro", "reqwest", "thiserror 2.0.17", + "thiserror-context", ] [[package]] @@ -607,6 +756,30 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jiff" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67e8da4c49d6d9909fe03361f9b620f58898859f5c7aded68351e85e71ecf50" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c84ee7f197eca9a86c6fd6cb771e55eb991632f15f2bc3ca6ec838929e6e78" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "jni" version = "0.21.1" @@ -689,7 +862,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" name = "minimal" version = "0.1.0" dependencies = [ + "env_logger", "isideload", + "tokio", ] [[package]] @@ -719,12 +894,27 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "openssl-probe" version = "0.2.0" @@ -771,6 +961,21 @@ dependencies = [ "plist", ] +[[package]] +name = "portable-atomic" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -913,6 +1118,35 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + [[package]] name = "reqwest" version = "0.13.1" @@ -1260,6 +1494,12 @@ dependencies = [ "thiserror-impl 2.0.17", ] +[[package]] +name = "thiserror-context" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d4cf778b0694c0adf030668380054b3a382f1e5e6a2e9a0b54bc5e677045bca" + [[package]] name = "thiserror-impl" version = "1.0.69" @@ -1340,18 +1580,30 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.48.0" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ "bytes", "libc", "mio", "pin-project-lite", "socket2", + "tokio-macros", "windows-sys 0.61.2", ] +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tokio-rustls" version = "0.26.4" @@ -1492,6 +1744,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "walkdir" version = "2.5.0" @@ -1622,6 +1880,41 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-link" version = "0.2.1" diff --git a/README.md b/README.md index 5055363..e5dd7cf 100644 --- a/README.md +++ b/README.md @@ -4,106 +4,8 @@ A Rust library for sideloading iOS applications using an Apple ID. Used in [CrossCode](https://github.com/nab138/CrossCode) and [iloader](https://github.com/nab138/iloader). -This also serves as a rust library for accessing Apple's private developer APIs. See [`developer_session.rs`](isideload/src/developer_session.rs) for details. - -## Disclaimer - -This package uses private Apple Developer APIs. Use at your own risk. - -## Usage - -To use isideload, add the following to your `Cargo.toml`: - -```toml -[dependencies] -# Make sure to use the latest version -isideload = { version = "0.1.21", features = ["vendored-openssl"] }# Optionally, the vendored feature can be enabled to avoid needing OpenSSL installed on your system. -idevice = { version = "0.1.46", features = ["usbmuxd", "ring"], default-features = false} # Reccomended to disable default features and enable ring to reduce the number of ssl stacks used -``` - -Then, you can use it like so: - -```rs -use std::{env, path::PathBuf, sync::Arc}; - -use idevice::usbmuxd::{UsbmuxdAddr, UsbmuxdConnection}; -use isideload::{ - AnisetteConfiguration, AppleAccount, SideloadConfiguration, - developer_session::DeveloperSession, sideload::sideload_app, -}; - -#[tokio::main] -async fn main() { - let args: Vec = env::args().collect(); - let app_path = PathBuf::from( - args.get(1) - .expect("Please provide the path to the app to install"), - ); - let apple_id = args - .get(2) - .expect("Please provide the Apple ID to use for installation"); - let apple_password = args.get(3).expect("Please provide the Apple ID password"); - - // You don't have to use usbmuxd, you can use any IdeviceProvider - let usbmuxd = UsbmuxdConnection::default().await; - if usbmuxd.is_err() { - panic!("Failed to connect to usbmuxd: {:?}", usbmuxd.err()); - } - let mut usbmuxd = usbmuxd.unwrap(); - - let devs = usbmuxd.get_devices().await.unwrap(); - if devs.is_empty() { - panic!("No devices found"); - } - - let provider = devs - .iter() - .next() - .unwrap() - .to_provider(UsbmuxdAddr::from_env_var().unwrap(), "isideload-demo"); - - // Change the anisette url and such here - // Note that right now only remote anisette servers are supported - let anisette_config = AnisetteConfiguration::default(); - - let get_2fa_code = || { - let mut code = String::new(); - println!("Enter 2FA code:"); - std::io::stdin().read_line(&mut code).unwrap(); - Ok(code.trim().to_string()) - }; - - let account = AppleAccount::login( - || Ok((apple_id.to_string(), apple_password.to_string())), - get_2fa_code, - anisette_config, - ) - .await - .unwrap(); - - let dev_session = DeveloperSession::new(Arc::new(account)); - - // You can change the machine name, store directory (for certs, anisette data, & provision files), and logger - let config = SideloadConfiguration::default().set_machine_name("isideload-demo".to_string()); - - sideload_app(&provider, &dev_session, app_path, config) - .await - .unwrap() -} -``` - -See [examples/minimal/src/main.rs](examples/minimal/src/main.rs). +This branch is home to isideload-next, the next major version of isideload. It features a redesigned API, improved error handling, better entitlement handling, and more. It is not ready! ## Licensing This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. - -## Credits - -- The amazing [idevice](https://github.com/jkcoxson/idevice) crate is used to communicate with the device - -- Packages from [`apple-private-apis`](https://github.com/SideStore/apple-private-apis), which is licensed under MPL-2.0, were used for authentication, but the original project was left unfinished. To support isideload, `apple-private-apis` was [forked](https://github.com/nab138/apple-private-apis) and modified to add missing features. With permission from the original developers, the fork was published to crates.io until the official project is published. - -- [apple-codesign](https://crates.io/crates/apple-codesign) was used for code signing, which is licensed under MPL-2.0. - -- [Sideloader](https://github.com/Dadoum/Sideloader) was used as a reference for how the private API endpoints work diff --git a/examples/minimal/Cargo.toml b/examples/minimal/Cargo.toml index 45d4403..4cf593c 100644 --- a/examples/minimal/Cargo.toml +++ b/examples/minimal/Cargo.toml @@ -4,4 +4,6 @@ version = "0.1.0" edition = "2024" [dependencies] -isideload = { path = "../../isideload" } \ No newline at end of file +isideload = { path = "../../isideload" } +env_logger = "0.11.8" +tokio = { version = "1.49.0", features = ["rt-multi-thread", "macros"] } diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index b9dadba..ae679b2 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -1,10 +1,15 @@ use std::{env, path::PathBuf}; -use isideload::auth::apple_account::AppleAccountBuilder; +use isideload::{ + anisette::remote_v3::RemoteV3AnisetteProvider, auth::apple_account::AppleAccountBuilder, +}; + +#[tokio::main] +async fn main() { + env_logger::init(); -fn main() { let args: Vec = env::args().collect(); - let app_path = PathBuf::from( + let _app_path = PathBuf::from( args.get(1) .expect("Please provide the path to the app to install"), ); @@ -13,8 +18,17 @@ fn main() { .expect("Please provide the Apple ID to use for installation"); let apple_password = args.get(3).expect("Please provide the Apple ID password"); - let account = AppleAccountBuilder::new(apple_id) + let get_2fa_code = || { + let mut code = String::new(); + println!("Enter 2FA code:"); + std::io::stdin().read_line(&mut code).unwrap(); + Ok(code.trim().to_string()) + }; + + let _account = AppleAccountBuilder::new(apple_id) .danger_debug(true) - .build() + .anisette(RemoteV3AnisetteProvider::default().set_serial_number("2".to_string())) + .login(apple_password, get_2fa_code) + .await .unwrap(); } diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 2ed76ae..5dd6ad1 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -18,5 +18,8 @@ install = ["dep:idevice"] idevice = { version = "0.1.51", optional = true } plist = "1.8.0" plist-macro = "0.1.0" +log = "0.4" reqwest = { version = "0.13.1", features = ["json", "gzip"] } thiserror = "2.0.17" +thiserror-context = "0.1.2" +chrono = "0.4.43" diff --git a/isideload/src/anisette/mod.rs b/isideload/src/anisette/mod.rs index d3d07a6..9f21bb5 100644 --- a/isideload/src/anisette/mod.rs +++ b/isideload/src/anisette/mod.rs @@ -1,6 +1,3 @@ +pub mod remote_v3; + pub trait AnisetteProvider {} - -// tmp -pub struct DefaultAnisetteProvider {} - -impl AnisetteProvider for DefaultAnisetteProvider {} diff --git a/isideload/src/anisette/remote_v3.rs b/isideload/src/anisette/remote_v3.rs new file mode 100644 index 0000000..17d7fb5 --- /dev/null +++ b/isideload/src/anisette/remote_v3.rs @@ -0,0 +1,92 @@ +use std::path::PathBuf; + +use chrono::{DateTime, SubsecRound, Utc}; +use reqwest::header::{HeaderMap, HeaderValue}; + +use crate::SideloadResult as Result; +use crate::anisette::AnisetteProvider; + +pub const DEFAULT_ANISETTE_V3_URL: &str = "https://ani.sidestore.io"; + +pub struct RemoteV3AnisetteProvider { + url: String, + config_path: PathBuf, + serial_number: String, +} + +impl RemoteV3AnisetteProvider { + /// Create a new RemoteV3AnisetteProvider with the given URL and config path + /// + /// # Arguments + /// - `url`: The URL of the remote anisette service + /// - `config_path`: The path to the config file + /// - `serial_number`: The serial number of the device + pub fn new(url: &str, config_path: PathBuf, serial_number: String) -> Self { + Self { + url: url.to_string(), + config_path, + serial_number, + } + } + + pub fn set_url(mut self, url: &str) -> RemoteV3AnisetteProvider { + self.url = url.to_string(); + self + } + + pub fn set_config_path(mut self, config_path: PathBuf) -> RemoteV3AnisetteProvider { + self.config_path = config_path; + self + } + + pub fn set_serial_number(mut self, serial_number: String) -> RemoteV3AnisetteProvider { + self.serial_number = serial_number; + self + } +} + +impl Default for RemoteV3AnisetteProvider { + fn default() -> Self { + Self::new(DEFAULT_ANISETTE_V3_URL, PathBuf::new(), "0".to_string()) + } +} + +#[derive(Debug)] +pub struct AnisetteData { + machine_id: String, + one_time_password: String, + routing_info: String, + device_description: String, + device_unique_identifier: String, + local_user_id: String, +} + +impl AnisetteData { + pub fn get_headers(&self, serial: String) -> Result { + let dt: DateTime = Utc::now().round_subsecs(0); + + let mut headers = HeaderMap::new(); + + for (key, value) in vec![ + ( + "X-Apple-I-Client-Time", + dt.format("%+").to_string().replace("+00:00", "Z"), + ), + ("X-Apple-I-SRL-NO", serial), + ("X-Apple-I-TimeZone", "UTC".to_string()), + ("X-Apple-Locale", "en_US".to_string()), + ("X-Apple-I-MD-RINFO", self.routing_info.clone()), + ("X-Apple-I-MD-LU", self.local_user_id.clone()), + ("X-Mme-Device-Id", self.device_unique_identifier.clone()), + ("X-Apple-I-MD", self.one_time_password.clone()), + ("X-Apple-I-MD-M", self.machine_id.clone()), + ("X-Mme-Client-Info", self.device_description.clone()), + ] { + headers.insert(key, HeaderValue::from_str(&value)?); + } + + Ok(headers) + } +} + +impl AnisetteProvider for RemoteV3AnisetteProvider {} diff --git a/isideload/src/auth/apple_account.rs b/isideload/src/auth/apple_account.rs index af88953..208ca3b 100644 --- a/isideload/src/auth/apple_account.rs +++ b/isideload/src/auth/apple_account.rs @@ -1,5 +1,11 @@ -use crate::Result; +use crate::{ + SideloadResult as Result, + anisette::{AnisetteProvider, remote_v3::RemoteV3AnisetteProvider}, + auth::grandslam::GrandSlam, +}; +use log::{info, warn}; use reqwest::{Certificate, ClientBuilder}; +use thiserror_context::Context; const APPLE_ROOT: &[u8] = include_bytes!("./apple_root.der"); @@ -7,13 +13,13 @@ pub struct AppleAccount { pub email: String, pub spd: Option, pub client: reqwest::Client, - pub anisette: Box, + pub anisette: Box, } -#[derive(Debug)] pub struct AppleAccountBuilder { email: String, debug: Option, + anisette: Option>, } impl AppleAccountBuilder { @@ -25,6 +31,7 @@ impl AppleAccountBuilder { Self { email: email.to_string(), debug: None, + anisette: None, } } @@ -37,14 +44,28 @@ impl AppleAccountBuilder { self } - /// Build the AppleAccount + pub fn anisette(mut self, anisette: impl AnisetteProvider + 'static) -> Self { + self.anisette = Some(Box::new(anisette)); + self + } + + /// Build the AppleAccount and log in /// + /// # Arguments + /// - `password`: The Apple ID password + /// - `two_factor_callback`: A callback function that returns the two-factor authentication code /// # Errors /// Returns an error if the reqwest client cannot be built - pub fn login(self) -> Result { + pub async fn login(self, _password: &str, _two_factor_callback: F) -> Result + where + F: Fn() -> Result, + { let debug = self.debug.unwrap_or(false); + let anisette = self + .anisette + .unwrap_or_else(|| Box::new(RemoteV3AnisetteProvider::default())); - AppleAccount::login(&self.email, debug) + AppleAccount::login(&self.email, debug, anisette).await } } @@ -60,14 +81,27 @@ impl AppleAccount { /// Log in to an Apple account with the given email /// /// Reccomended to use the AppleAccountBuilder instead - pub fn login(email: &str, debug: bool) -> Result { + pub async fn login( + email: &str, + debug: bool, + anisette: Box, + ) -> Result { + info!("Logging in to apple ID: {}", email); + if debug { + warn!("Debug mode enabled: this is a security risk!"); + } let client = Self::build_client(debug)?; + let mut gs = GrandSlam::new(&client); + gs.get_url_bag() + .await + .context("Failed to get URL bag from GrandSlam")?; + Ok(AppleAccount { email: email.to_string(), spd: None, client, - anisette: Box::new(crate::anisette::DefaultAnisetteProvider {}), + anisette, }) } diff --git a/isideload/src/auth/grandslam.rs b/isideload/src/auth/grandslam.rs new file mode 100644 index 0000000..593b5f2 --- /dev/null +++ b/isideload/src/auth/grandslam.rs @@ -0,0 +1,64 @@ +use crate::SideloadResult as Result; +use log::debug; +use plist::Dictionary; +use plist_macro::pretty_print_dictionary; +use reqwest::header::HeaderValue; + +const URL_BAG: &str = "https://gsa.apple.com/grandslam/GsService2/lookup"; + +pub struct GrandSlam<'a> { + client: &'a reqwest::Client, + url_bag: Option, +} + +impl<'a> GrandSlam<'a> { + /// Create a new GrandSlam instance + /// + /// # Arguments + /// - `client`: The reqwest client to use for requests + pub fn new(client: &'a reqwest::Client) -> Self { + Self { + client, + url_bag: None, + } + } + + /// Get the URL bag from GrandSlam + pub async fn get_url_bag(&mut self) -> Result<&Dictionary> { + if self.url_bag.is_none() { + debug!("Fetching URL bag from GrandSlam"); + let resp = self + .client + .get(URL_BAG) + .headers(Self::base_headers()) + .send() + .await? + .text() + .await?; + let dict: Dictionary = plist::from_bytes(resp.as_bytes())?; + debug!("{}", pretty_print_dictionary(&dict)); + self.url_bag = Some(dict); + } + Ok(self.url_bag.as_ref().unwrap()) + } + + fn base_headers() -> reqwest::header::HeaderMap { + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("Context-Type", HeaderValue::from_static("text/x-xml-plist")); + headers.insert("Accept", HeaderValue::from_static("text/x-xml-plist")); + headers.insert( + "X-Mme-Client-Info", + HeaderValue::from_static( + " ", + ), + ); + headers.insert("User-Agent", HeaderValue::from_static("Xcode")); + headers.insert("X-Xcode-Version", HeaderValue::from_static("14.2 (14C18)")); + headers.insert( + "X-Apple-App-Info", + HeaderValue::from_static("com.apple.gs.xcode.auth"), + ); + + headers + } +} diff --git a/isideload/src/auth/mod.rs b/isideload/src/auth/mod.rs index 7af46c6..9aa7ce1 100644 --- a/isideload/src/auth/mod.rs +++ b/isideload/src/auth/mod.rs @@ -1 +1,2 @@ pub mod apple_account; +pub mod grandslam; diff --git a/isideload/src/lib.rs b/isideload/src/lib.rs index c5b0051..00ca26b 100644 --- a/isideload/src/lib.rs +++ b/isideload/src/lib.rs @@ -1,12 +1,21 @@ use thiserror::Error as ThisError; +use thiserror_context::{Context, impl_context}; pub mod anisette; pub mod auth; #[derive(Debug, ThisError)] -pub enum Error { - #[error("Reqwest error: {0}")] +pub enum ErrorInner { + #[error("Failed sending request: {0}")] Reqwest(#[from] reqwest::Error), + + #[error("Failed parsing plist: {0}")] + Plist(#[from] plist::Error), + + #[error("Invalid Header: {0}")] + InvalidHeader(#[from] reqwest::header::InvalidHeaderValue), } -pub type Result = std::result::Result; +impl_context!(Error(ErrorInner)); + +pub type SideloadResult = std::result::Result; From 1f61c1073140e6f83e2665b5437a4b8e5d025752 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 25 Jan 2026 00:33:03 -0500 Subject: [PATCH 10/71] Anisette provisioning works --- .gitignore | 2 + Cargo.lock | 675 +++++++++++++--------- examples/minimal/Cargo.toml | 3 +- examples/minimal/src/main.rs | 19 +- isideload/Cargo.toml | 18 +- isideload/src/anisette/mod.rs | 21 +- isideload/src/anisette/remote_v3.rs | 92 --- isideload/src/anisette/remote_v3/mod.rs | 393 +++++++++++++ isideload/src/anisette/remote_v3/state.rs | 81 +++ isideload/src/auth/apple_account.rs | 63 +- isideload/src/auth/grandslam.rs | 86 ++- isideload/src/lib.rs | 20 +- isideload/src/util/mod.rs | 1 + isideload/src/util/plist.rs | 9 + 14 files changed, 1021 insertions(+), 462 deletions(-) delete mode 100644 isideload/src/anisette/remote_v3.rs create mode 100644 isideload/src/anisette/remote_v3/mod.rs create mode 100644 isideload/src/anisette/remote_v3/state.rs create mode 100644 isideload/src/util/mod.rs create mode 100644 isideload/src/util/plist.rs diff --git a/.gitignore b/.gitignore index eb5a316..7b3aad1 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ target + +state.plist \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index b83065f..188bca2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,15 +8,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - [[package]] name = "android_system_properties" version = "0.1.5" @@ -26,69 +17,29 @@ dependencies = [ "libc", ] -[[package]] -name = "anstream" -version = "0.6.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" - -[[package]] -name = "anstyle-parse" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys 0.61.2", -] - [[package]] name = "async-compression" -version = "0.4.36" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98ec5f6c2f8bc326c994cb9e241cc257ddaba9afa8555a43cffbb5dd86efaa37" +checksum = "d10e4f991a553474232bc0a31799f6d24b034a84c0971d80d2e2f78b2e576e40" dependencies = [ "compression-codecs", "compression-core", - "futures-core", "pin-project-lite", "tokio", ] +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -103,9 +54,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.15.2" +version = "1.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88aab2464f1f25453baa7a07c84c5b7684e274054ba06817f382357f77a288" +checksum = "e84ce723ab67259cfeb9877c6a639ee9eb7a27b28123abd71db7f0d5d0cc9d86" dependencies = [ "aws-lc-sys", "zeroize", @@ -113,9 +64,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45afffdee1e7c9126814751f88dddc747f41d91da16c9551a0f1e8a11e788a1" +checksum = "43a442ece363113bd4bd4c8b18977a7798dd4d3c3383f34fb61936960e8f4ad8" dependencies = [ "cc", "cmake", @@ -135,6 +86,15 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bumpalo" version = "3.19.1" @@ -149,9 +109,9 @@ checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "cc" -version = "1.2.49" +version = "1.2.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" +checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" dependencies = [ "find-msvc-tools", "jobserver", @@ -192,19 +152,13 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b042e5d8a74ae91bb0961acd039822472ec99f8ab0948cbf6d1369588f8be586" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" dependencies = [ "cc", ] -[[package]] -name = "colorchoice" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" - [[package]] name = "combine" version = "4.6.7" @@ -217,9 +171,9 @@ dependencies = [ [[package]] name = "compression-codecs" -version = "0.4.35" +version = "0.4.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0f7ac3e5b97fdce45e8922fb05cae2c37f7bbd63d30dd94821dacfd8f3f2bf2" +checksum = "00828ba6fd27b45a448e57dbfe84f1029d4c9f26b368157e9a448a5f49a2ec2a" dependencies = [ "compression-core", "flate2", @@ -258,6 +212,15 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -267,6 +230,22 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "data-encoding" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" + [[package]] name = "deranged" version = "0.5.5" @@ -276,6 +255,16 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -302,29 +291,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "env_filter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.11.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" -dependencies = [ - "anstream", - "anstyle", - "env_filter", - "jiff", - "log", -] - [[package]] name = "equivalent" version = "1.0.2" @@ -333,15 +299,15 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "find-msvc-tools" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" [[package]] name = "flate2" -version = "1.1.5" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" +checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" dependencies = [ "crc32fast", "miniz_oxide", @@ -383,6 +349,17 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.31" @@ -402,16 +379,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", + "futures-macro", + "futures-sink", "futures-task", "pin-project-lite", "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", ] [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "js-sys", @@ -459,6 +449,12 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "http" version = "1.4.0" @@ -669,15 +665,16 @@ dependencies = [ [[package]] name = "idevice" -version = "0.1.51" +version = "0.1.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d99be8d1969168237987908274fe41bcb2c8a1f9a37aa12fd15d5713acb9dd2b" +checksum = "f4031af51250d2f22f61a0d7fb7ea71ba8b6144b2b9dd3b7ee4a931fccbd1ec0" dependencies = [ "base64", "plist", + "plist-macro", "rustls", "serde", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tokio-rustls", "tracing", @@ -706,9 +703,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown", @@ -730,55 +727,35 @@ dependencies = [ "serde", ] -[[package]] -name = "is_terminal_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - [[package]] name = "isideload" version = "0.2.0" dependencies = [ + "async-trait", + "base64", "chrono", + "futures-util", + "hex", "idevice", - "log", "plist", "plist-macro", + "rand", "reqwest", - "thiserror 2.0.17", - "thiserror-context", + "rootcause", + "serde", + "serde_json", + "sha2", + "thiserror 2.0.18", + "tokio-tungstenite", + "tracing", + "uuid", ] [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" - -[[package]] -name = "jiff" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67e8da4c49d6d9909fe03361f9b620f58898859f5c7aded68351e85e71ecf50" -dependencies = [ - "jiff-static", - "log", - "portable-atomic", - "portable-atomic-util", - "serde_core", -] - -[[package]] -name = "jiff-static" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c84ee7f197eca9a86c6fd6cb771e55eb991632f15f2bc3ca6ec838929e6e78" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jni" @@ -814,19 +791,25 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.83" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] -name = "libc" -version = "0.2.178" +name = "lazy_static" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "litemap" @@ -862,9 +845,10 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" name = "minimal" version = "0.1.0" dependencies = [ - "env_logger", "isideload", "tokio", + "tracing", + "tracing-subscriber", ] [[package]] @@ -888,6 +872,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -909,17 +902,11 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - [[package]] name = "openssl-probe" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "percent-encoding" @@ -954,28 +941,13 @@ dependencies = [ [[package]] name = "plist-macro" -version = "0.1.0" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb72007326fe20721ef27304fcf2d1bd5877b92d13dbd8df735fd33407e31c2a" +checksum = "8888e02e251eba3258cc58fb79f0d8675c34b3428749e738562d58a0271bf035" dependencies = [ "plist", ] -[[package]] -name = "portable-atomic" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" - -[[package]] -name = "portable-atomic-util" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" -dependencies = [ - "portable-atomic", -] - [[package]] name = "potential_utf" version = "0.1.4" @@ -1002,9 +974,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -1032,7 +1004,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", "web-time", @@ -1054,7 +1026,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.17", + "thiserror 2.0.18", "tinyvec", "tracing", "web-time", @@ -1076,9 +1048,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.42" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] @@ -1111,42 +1083,13 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ "getrandom 0.3.4", ] -[[package]] -name = "regex" -version = "1.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" - [[package]] name = "reqwest" version = "0.13.1" @@ -1195,12 +1138,34 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.16", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", ] +[[package]] +name = "rootcause" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a751633dcb95a6b1c954f0fa15c2afd9b4802640f8045432f68a1f4bde4b871" +dependencies = [ + "hashbrown", + "indexmap", + "rootcause-internals", + "rustc-hash", + "triomphe", +] + +[[package]] +name = "rootcause-internals" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9eeddca0d656f1a58ce3fc3f41b0b877a7e760460108712ad39b60181fdcb3e" +dependencies = [ + "triomphe", +] + [[package]] name = "rustc-hash" version = "2.1.1" @@ -1209,9 +1174,9 @@ checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustls" -version = "0.23.35" +version = "0.23.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" dependencies = [ "aws-lc-rs", "once_cell", @@ -1235,9 +1200,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ "web-time", "zeroize", @@ -1272,9 +1237,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.8" +version = "0.103.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" dependencies = [ "aws-lc-rs", "ring", @@ -1288,12 +1253,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - [[package]] name = "same-file" version = "1.0.6" @@ -1367,15 +1326,46 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", ] [[package]] @@ -1426,9 +1416,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.111" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -1487,19 +1477,13 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] -[[package]] -name = "thiserror-context" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d4cf778b0694c0adf030668380054b3a382f1e5e6a2e9a0b54bc5e677045bca" - [[package]] name = "thiserror-impl" version = "1.0.69" @@ -1513,9 +1497,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -1523,31 +1507,40 @@ dependencies = [ ] [[package]] -name = "time" -version = "0.3.44" +name = "thread_local" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "time" +version = "0.3.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" +checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" [[package]] name = "time-macros" -version = "0.2.24" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd" dependencies = [ "num-conv", "time-core", @@ -1614,6 +1607,22 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite", + "webpki-roots 0.26.11", +] + [[package]] name = "tokio-util" version = "0.7.18" @@ -1629,9 +1638,9 @@ dependencies = [ [[package]] name = "tower" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", @@ -1679,9 +1688,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -1701,19 +1710,76 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.35" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", + "valuable", ] +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "triomphe" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" + [[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror 2.0.18", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + [[package]] name = "unicode-ident" version = "1.0.22" @@ -1738,6 +1804,12 @@ dependencies = [ "serde", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -1745,10 +1817,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] -name = "utf8parse" -version = "0.2.2" +name = "uuid" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "walkdir" @@ -1777,18 +1865,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" dependencies = [ "cfg-if", "once_cell", @@ -1799,11 +1887,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.56" +version = "0.4.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" dependencies = [ "cfg-if", + "futures-util", "js-sys", "once_cell", "wasm-bindgen", @@ -1812,9 +1901,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1822,9 +1911,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" dependencies = [ "bumpalo", "proc-macro2", @@ -1835,18 +1924,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.83" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" dependencies = [ "js-sys", "wasm-bindgen", @@ -1871,6 +1960,24 @@ dependencies = [ "rustls-pki-types", ] +[[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.5", +] + +[[package]] +name = "webpki-roots" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "winapi-util" version = "0.1.11" @@ -2174,9 +2281,9 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] name = "writeable" @@ -2209,18 +2316,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.31" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.31" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ "proc-macro2", "quote", @@ -2286,3 +2393,9 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zmij" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65" diff --git a/examples/minimal/Cargo.toml b/examples/minimal/Cargo.toml index 4cf593c..a96136b 100644 --- a/examples/minimal/Cargo.toml +++ b/examples/minimal/Cargo.toml @@ -5,5 +5,6 @@ edition = "2024" [dependencies] isideload = { path = "../../isideload" } -env_logger = "0.11.8" tokio = { version = "1.49.0", features = ["rt-multi-thread", "macros"] } +tracing = "0.1.44" +tracing-subscriber = "0.3.22" diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index ae679b2..b29fd5a 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -3,10 +3,15 @@ use std::{env, path::PathBuf}; use isideload::{ anisette::remote_v3::RemoteV3AnisetteProvider, auth::apple_account::AppleAccountBuilder, }; +use tracing::Level; +use tracing_subscriber::FmtSubscriber; #[tokio::main] async fn main() { - env_logger::init(); + let subscriber = FmtSubscriber::builder() + .with_max_level(Level::DEBUG) + .finish(); + tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); let args: Vec = env::args().collect(); let _app_path = PathBuf::from( @@ -22,13 +27,17 @@ async fn main() { let mut code = String::new(); println!("Enter 2FA code:"); std::io::stdin().read_line(&mut code).unwrap(); - Ok(code.trim().to_string()) + Some(code.trim().to_string()) }; - let _account = AppleAccountBuilder::new(apple_id) + let account = AppleAccountBuilder::new(apple_id) .danger_debug(true) .anisette(RemoteV3AnisetteProvider::default().set_serial_number("2".to_string())) .login(apple_password, get_2fa_code) - .await - .unwrap(); + .await; + + match account { + Ok(_account) => println!("Successfully logged in to Apple ID"), + Err(e) => eprintln!("Failed to log in to Apple ID: {}", e), + } } diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 5dd6ad1..4e924fe 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -16,10 +16,20 @@ install = ["dep:idevice"] [dependencies] idevice = { version = "0.1.51", optional = true } -plist = "1.8.0" -plist-macro = "0.1.0" -log = "0.4" +plist = "1.8" +plist-macro = "0.1.3" reqwest = { version = "0.13.1", features = ["json", "gzip"] } thiserror = "2.0.17" -thiserror-context = "0.1.2" chrono = "0.4.43" +async-trait = "0.1.89" +serde = "1.0.228" +rand = "0.9.2" +uuid = "1.19.0" +sha2 = "0.10.9" +tracing = "0.1.44" +tokio-tungstenite = { version = "0.28.0", features = ["rustls-tls-webpki-roots"] } +rootcause = "0.11.1" +futures-util = "0.3.31" +serde_json = "1.0.149" +base64 = "0.22.1" +hex = "0.4.3" diff --git a/isideload/src/anisette/mod.rs b/isideload/src/anisette/mod.rs index 9f21bb5..196fe25 100644 --- a/isideload/src/anisette/mod.rs +++ b/isideload/src/anisette/mod.rs @@ -1,3 +1,22 @@ pub mod remote_v3; -pub trait AnisetteProvider {} +use crate::auth::grandslam::GrandSlam; +use rootcause::prelude::*; +use serde::Deserialize; +use std::collections::HashMap; + +#[derive(Deserialize, Debug, Clone)] +pub struct AnisetteClientInfo { + pub client_info: String, + pub user_agent: String, +} + +#[async_trait::async_trait] +pub trait AnisetteProvider { + async fn get_anisette_headers( + &mut self, + gs: &mut GrandSlam, + ) -> Result, Report>; + + async fn get_client_info(&mut self) -> Result; +} diff --git a/isideload/src/anisette/remote_v3.rs b/isideload/src/anisette/remote_v3.rs deleted file mode 100644 index 17d7fb5..0000000 --- a/isideload/src/anisette/remote_v3.rs +++ /dev/null @@ -1,92 +0,0 @@ -use std::path::PathBuf; - -use chrono::{DateTime, SubsecRound, Utc}; -use reqwest::header::{HeaderMap, HeaderValue}; - -use crate::SideloadResult as Result; -use crate::anisette::AnisetteProvider; - -pub const DEFAULT_ANISETTE_V3_URL: &str = "https://ani.sidestore.io"; - -pub struct RemoteV3AnisetteProvider { - url: String, - config_path: PathBuf, - serial_number: String, -} - -impl RemoteV3AnisetteProvider { - /// Create a new RemoteV3AnisetteProvider with the given URL and config path - /// - /// # Arguments - /// - `url`: The URL of the remote anisette service - /// - `config_path`: The path to the config file - /// - `serial_number`: The serial number of the device - pub fn new(url: &str, config_path: PathBuf, serial_number: String) -> Self { - Self { - url: url.to_string(), - config_path, - serial_number, - } - } - - pub fn set_url(mut self, url: &str) -> RemoteV3AnisetteProvider { - self.url = url.to_string(); - self - } - - pub fn set_config_path(mut self, config_path: PathBuf) -> RemoteV3AnisetteProvider { - self.config_path = config_path; - self - } - - pub fn set_serial_number(mut self, serial_number: String) -> RemoteV3AnisetteProvider { - self.serial_number = serial_number; - self - } -} - -impl Default for RemoteV3AnisetteProvider { - fn default() -> Self { - Self::new(DEFAULT_ANISETTE_V3_URL, PathBuf::new(), "0".to_string()) - } -} - -#[derive(Debug)] -pub struct AnisetteData { - machine_id: String, - one_time_password: String, - routing_info: String, - device_description: String, - device_unique_identifier: String, - local_user_id: String, -} - -impl AnisetteData { - pub fn get_headers(&self, serial: String) -> Result { - let dt: DateTime = Utc::now().round_subsecs(0); - - let mut headers = HeaderMap::new(); - - for (key, value) in vec![ - ( - "X-Apple-I-Client-Time", - dt.format("%+").to_string().replace("+00:00", "Z"), - ), - ("X-Apple-I-SRL-NO", serial), - ("X-Apple-I-TimeZone", "UTC".to_string()), - ("X-Apple-Locale", "en_US".to_string()), - ("X-Apple-I-MD-RINFO", self.routing_info.clone()), - ("X-Apple-I-MD-LU", self.local_user_id.clone()), - ("X-Mme-Device-Id", self.device_unique_identifier.clone()), - ("X-Apple-I-MD", self.one_time_password.clone()), - ("X-Apple-I-MD-M", self.machine_id.clone()), - ("X-Mme-Client-Info", self.device_description.clone()), - ] { - headers.insert(key, HeaderValue::from_str(&value)?); - } - - Ok(headers) - } -} - -impl AnisetteProvider for RemoteV3AnisetteProvider {} diff --git a/isideload/src/anisette/remote_v3/mod.rs b/isideload/src/anisette/remote_v3/mod.rs new file mode 100644 index 0000000..bc63071 --- /dev/null +++ b/isideload/src/anisette/remote_v3/mod.rs @@ -0,0 +1,393 @@ +mod state; + +use std::collections::HashMap; +use std::fs; +use std::path::PathBuf; + +use base64::prelude::*; +use chrono::{DateTime, SubsecRound, Utc}; +use plist_macro::plist; +use reqwest::header::{HeaderMap, HeaderValue}; +use rootcause::prelude::*; +use serde::Deserialize; +use tokio_tungstenite::tungstenite::Message; +use tracing::{debug, info}; + +use crate::anisette::remote_v3::state::AnisetteState; +use crate::anisette::{AnisetteClientInfo, AnisetteProvider}; +use crate::auth::grandslam::GrandSlam; +use crate::util::plist::plist_to_xml_string; +use futures_util::{SinkExt, StreamExt}; + +pub const DEFAULT_ANISETTE_V3_URL: &str = "https://ani.sidestore.io"; + +#[derive(Debug)] +pub struct AnisetteData { + machine_id: String, + one_time_password: String, + routing_info: String, + device_description: String, + device_unique_identifier: String, + local_user_id: String, +} + +impl AnisetteData { + pub fn get_headers(&self, serial: String) -> HashMap { + let dt: DateTime = Utc::now().round_subsecs(0); + + HashMap::from_iter( + [ + ( + "X-Apple-I-Client-Time".to_string(), + dt.format("%+").to_string().replace("+00:00", "Z"), + ), + ("X-Apple-I-SRL-NO".to_string(), serial), + ("X-Apple-I-TimeZone".to_string(), "UTC".to_string()), + ("X-Apple-Locale".to_string(), "en_US".to_string()), + ("X-Apple-I-MD-RINFO".to_string(), self.routing_info.clone()), + ("X-Apple-I-MD-LU".to_string(), self.local_user_id.clone()), + ( + "X-Mme-Device-Id".to_string(), + self.device_unique_identifier.clone(), + ), + ("X-Apple-I-MD".to_string(), self.one_time_password.clone()), + ("X-Apple-I-MD-M".to_string(), self.machine_id.clone()), + ( + "X-Mme-Client-Info".to_string(), + self.device_description.clone(), + ), + ] + .into_iter(), + ) + } +} + +pub struct RemoteV3AnisetteProvider { + pub state: Option, + url: String, + config_path: PathBuf, + serial_number: String, + client_info: Option, + client: reqwest::Client, +} + +impl RemoteV3AnisetteProvider { + /// Create a new RemoteV3AnisetteProvider with the given URL and config path + /// + /// # Arguments + /// - `url`: The URL of the remote anisette service + /// - `config_path`: The path to the config file + /// - `serial_number`: The serial number of the device + /// + pub fn new(url: &str, config_path: PathBuf, serial_number: String) -> Self { + Self { + state: None, + url: url.to_string(), + config_path, + serial_number, + client_info: None, + client: reqwest::ClientBuilder::new() + .danger_accept_invalid_certs(true) + .build() + .unwrap(), + } + } + + pub fn set_url(mut self, url: &str) -> RemoteV3AnisetteProvider { + self.url = url.to_string(); + self + } + + pub fn set_config_path(mut self, config_path: PathBuf) -> RemoteV3AnisetteProvider { + self.config_path = config_path; + self + } + + pub fn set_serial_number(mut self, serial_number: String) -> RemoteV3AnisetteProvider { + self.serial_number = serial_number; + self + } +} + +impl Default for RemoteV3AnisetteProvider { + fn default() -> Self { + Self::new(DEFAULT_ANISETTE_V3_URL, PathBuf::new(), "0".to_string()) + } +} + +#[async_trait::async_trait] +impl AnisetteProvider for RemoteV3AnisetteProvider { + async fn get_anisette_headers( + &mut self, + gs: &mut GrandSlam, + ) -> Result, Report> { + let state = self.get_state(gs).await?; + + unimplemented!() + } + + async fn get_client_info(&mut self) -> Result { + self.ensure_client_info().await?; + Ok(self.client_info.as_ref().unwrap().clone()) + } +} + +impl RemoteV3AnisetteProvider { + async fn ensure_client_info(&mut self) -> Result<(), Report> { + if self.client_info.is_none() { + let resp = self + .client + .get(format!("{}/v3/client_info", self.url)) + .send() + .await? + .error_for_status()? + .json::() + .await?; + + self.client_info = Some(resp); + } + + debug!("Got client client_info: {:?}", self.client_info); + + Ok(()) + } + + async fn get_state(&mut self, gs: &mut GrandSlam) -> Result<&mut AnisetteState, Report> { + let state_path = self.config_path.join("state.plist"); + fs::create_dir_all(&self.config_path)?; + if self.state.is_none() { + if let Ok(state) = plist::from_file(&state_path) { + debug!("Loaded existing anisette state from {:?}", state_path); + self.state = Some(state); + } else { + debug!("No existing anisette state found"); + self.state = Some(AnisetteState::new()); + } + } + + let state = self.state.as_mut().unwrap(); + if !state.is_provisioned() { + info!("Provisioning required..."); + Self::provision(state, gs, &self.url) + .await + .context("Failed to provision")?; + } + plist::to_file_xml(&state_path, &state)?; + + Ok(state) + } + + async fn provisioning_headers( + state: &AnisetteState, + gs: &mut GrandSlam, + ) -> Result { + let mut headers = HeaderMap::new(); + headers.insert( + "X-Apple-I-MD-LU", + HeaderValue::from_str(&hex::encode(state.get_md_lu()))?, + ); + headers.insert( + "X-Apple-I-Client-Time", + HeaderValue::from_str( + &Utc::now() + .round_subsecs(0) + .format("%+") + .to_string() + .replace("+00:00", "Z"), + )?, + ); + headers.insert("X-Apple-I-TimeZone", HeaderValue::from_static("UTC")); + headers.insert("X-Apple-Locale", HeaderValue::from_static("en_US")); + headers.insert( + "X-Mme-Device-Id", + HeaderValue::from_str(&state.get_device_id())?, + ); + + Ok(headers) + } + async fn provision( + state: &mut AnisetteState, + gs: &mut GrandSlam, + url: &str, + ) -> Result<(), Report> { + info!("Starting provisioning"); + let urls = gs.get_url_bag().await?; + + let start_provisioning = urls + .get("midStartProvisioning") + .and_then(|v| v.as_string()) + .ok_or(report!("Missing URL bag entry for midStartProvisioning"))? + .to_string(); + let end_provisioning = urls + .get("midFinishProvisioning") + .and_then(|v| v.as_string()) + .ok_or(report!("Missing URL bag entry for midFinishProvisioning"))? + .to_string(); + + let websocket_url = format!("{}/v3/provisioning_session", url) + .replace("https://", "wss://") + .replace("http://", "ws://"); + let (mut ws_stream, _) = tokio_tungstenite::connect_async(&websocket_url) + .await + .context("Failed to connect anisette provisioning socket")?; + + loop { + let Some(msg) = ws_stream.next().await else { + continue; + }; + let msg = msg.context("Failed to read anisette provisioning socket message")?; + if msg.is_close() { + bail!("Anisette provisioning socket closed unexpectedly"); + } + let msg = msg + .into_text() + .context("Failed to parse provisioning message")?; + + debug!("Received provisioning message: {}", msg); + let provision_msg: ProvisioningMessage = + serde_json::from_str(&msg).context("Unknown provisioning message")?; + + match provision_msg { + ProvisioningMessage::GiveIdentifier => { + ws_stream + .send(Message::Text( + serde_json::json!({ + "identifier": BASE64_STANDARD.encode(&state.keychain_identifier), + }) + .to_string() + .into(), + )) + .await + .context("Failed to send identifier")?; + } + ProvisioningMessage::GiveStartProvisioningData => { + let body = plist!(dict { + "Header": {}, + "Request": {} + }); + + let resp = gs + .post(&start_provisioning)? + .headers(Self::provisioning_headers(state, gs).await?) + .body(plist_to_xml_string(&body)) + .send() + .await + .context("Failed to send start provisioning request")? + .error_for_status() + .context("Start provisioning request returned error")? + .text() + .await + .context("Failed to read start provisioning response text")?; + + let resp_plist: plist::Dictionary = plist::from_bytes(resp.as_bytes()) + .context("Failed to parse start provisioning response plist")?; + + let spim = resp_plist + .get("Response") + .and_then(|v| v.as_dictionary()) + .and_then(|d| d.get("spim")) + .and_then(|v| v.as_string()) + .ok_or(report!("Start provisioning response missing spim"))?; + + ws_stream + .send(Message::Text( + serde_json::json!({ + "spim": spim, + }) + .to_string() + .into(), + )) + .await + .context("Failed to send start provisioning data")?; + } + ProvisioningMessage::GiveEndProvisioningData { cpim } => { + let body = plist!(dict { + "Header": {}, + "Request": { + "cpim": cpim, + } + }); + + let resp = gs + .post(&end_provisioning)? + .headers(Self::provisioning_headers(state, gs).await?) + .body(plist_to_xml_string(&body)) + .send() + .await + .context("Failed to send end provisioning request")? + .error_for_status() + .context("End provisioning request returned error")? + .text() + .await + .context("Failed to read end provisioning response text")?; + + let resp_plist: plist::Dictionary = plist::from_bytes(resp.as_bytes()) + .context("Failed to parse end provisioning response plist")?; + let response = resp_plist + .get("Response") + .and_then(|v| v.as_dictionary()) + .ok_or(report!( + "End provisioning response missing Response dictionary" + ))?; + + ws_stream + .send(Message::Text( + serde_json::json!({ + "ptm": response + .get("ptm") + .and_then(|v| v.as_string()) + .ok_or(report!("End provisioning response missing ptm"))?, + "tk": response + .get("tk") + .and_then(|v| v.as_string()) + .ok_or(report!("End provisioning response missing tk"))?, + }) + .to_string() + .into(), + )) + .await + .context("Failed to send start provisioning data")?; + } + ProvisioningMessage::ProvisioningSuccess { adi_pb } => { + state.adi_pb = Some(BASE64_STANDARD.decode(adi_pb)?); + ws_stream.close(None).await?; + info!("Provisioning successful"); + break; + } + ProvisioningMessage::Timeout => bail!("Anisette provisioning timed out"), + ProvisioningMessage::InvalidIdentifier => { + bail!("Anisette provisioning failed: invalid identifier") + } + ProvisioningMessage::StartProvisioningError { message } => { + return Err( + report!("Anisette provisioning failed: start provisioning error") + .attach(message) + .into(), + ); + } + ProvisioningMessage::EndProvisioningError { message } => { + return Err( + report!("Anisette provisioning failed: end provisioning error") + .attach(message) + .into(), + ); + } + } + } + + Ok(()) + } +} + +#[derive(Deserialize)] +#[serde(tag = "result")] +enum ProvisioningMessage { + GiveIdentifier, + GiveStartProvisioningData, + GiveEndProvisioningData { cpim: String }, + ProvisioningSuccess { adi_pb: String }, + Timeout, + InvalidIdentifier, + StartProvisioningError { message: String }, + EndProvisioningError { message: String }, +} diff --git a/isideload/src/anisette/remote_v3/state.rs b/isideload/src/anisette/remote_v3/state.rs new file mode 100644 index 0000000..d87499b --- /dev/null +++ b/isideload/src/anisette/remote_v3/state.rs @@ -0,0 +1,81 @@ +// Serialization/Desieralization borrowed from https://github.com/SideStore/apple-private-apis/blob/master/omnisette/src/remote_anisette_v3.rs + +use plist::Data; +use rand::Rng; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use sha2::{Digest, Sha256}; +use uuid::Uuid; + +fn bin_serialize(x: &[u8], s: S) -> Result +where + S: Serializer, +{ + s.serialize_bytes(x) +} + +fn bin_serialize_opt(x: &Option>, s: S) -> Result +where + S: Serializer, +{ + x.clone().map(|i| Data::new(i)).serialize(s) +} + +fn bin_deserialize_opt<'de, D>(d: D) -> Result>, D::Error> +where + D: Deserializer<'de>, +{ + let s: Option = Deserialize::deserialize(d)?; + Ok(s.map(|i| i.into())) +} + +fn bin_deserialize_16<'de, D>(d: D) -> Result<[u8; 16], D::Error> +where + D: Deserializer<'de>, +{ + let s: Data = Deserialize::deserialize(d)?; + let s: Vec = s.into(); + Ok(s.try_into().unwrap()) +} + +#[derive(Serialize, Deserialize)] +pub struct AnisetteState { + #[serde( + serialize_with = "bin_serialize", + deserialize_with = "bin_deserialize_16" + )] + pub keychain_identifier: [u8; 16], + #[serde( + serialize_with = "bin_serialize_opt", + deserialize_with = "bin_deserialize_opt" + )] + pub adi_pb: Option>, +} + +impl Default for AnisetteState { + fn default() -> Self { + AnisetteState { + keychain_identifier: rand::rng().random::<[u8; 16]>(), + adi_pb: None, + } + } +} + +impl AnisetteState { + pub fn new() -> AnisetteState { + AnisetteState::default() + } + + pub fn is_provisioned(&self) -> bool { + self.adi_pb.is_some() + } + + pub fn get_md_lu(&self) -> [u8; 32] { + let mut hasher = Sha256::new(); + hasher.update(&self.keychain_identifier); + hasher.finalize().into() + } + + pub fn get_device_id(&self) -> String { + Uuid::from_bytes(self.keychain_identifier).to_string() + } +} diff --git a/isideload/src/auth/apple_account.rs b/isideload/src/auth/apple_account.rs index 208ca3b..281e820 100644 --- a/isideload/src/auth/apple_account.rs +++ b/isideload/src/auth/apple_account.rs @@ -1,19 +1,15 @@ use crate::{ - SideloadResult as Result, anisette::{AnisetteProvider, remote_v3::RemoteV3AnisetteProvider}, auth::grandslam::GrandSlam, }; -use log::{info, warn}; -use reqwest::{Certificate, ClientBuilder}; -use thiserror_context::Context; - -const APPLE_ROOT: &[u8] = include_bytes!("./apple_root.der"); +use rootcause::prelude::*; +use tracing::{info, warn}; pub struct AppleAccount { pub email: String, pub spd: Option, - pub client: reqwest::Client, pub anisette: Box, + pub grandslam_client: GrandSlam, } pub struct AppleAccountBuilder { @@ -56,16 +52,20 @@ impl AppleAccountBuilder { /// - `two_factor_callback`: A callback function that returns the two-factor authentication code /// # Errors /// Returns an error if the reqwest client cannot be built - pub async fn login(self, _password: &str, _two_factor_callback: F) -> Result + pub async fn login( + self, + password: &str, + two_factor_callback: F, + ) -> Result where - F: Fn() -> Result, + F: Fn() -> Option, { let debug = self.debug.unwrap_or(false); let anisette = self .anisette .unwrap_or_else(|| Box::new(RemoteV3AnisetteProvider::default())); - AppleAccount::login(&self.email, debug, anisette).await + AppleAccount::login(&self.email, password, two_factor_callback, anisette, debug).await } } @@ -83,43 +83,36 @@ impl AppleAccount { /// Reccomended to use the AppleAccountBuilder instead pub async fn login( email: &str, + password: &str, + two_factor_callback: impl Fn() -> Option, + mut anisette: Box, debug: bool, - anisette: Box, - ) -> Result { + ) -> Result { info!("Logging in to apple ID: {}", email); if debug { warn!("Debug mode enabled: this is a security risk!"); } - let client = Self::build_client(debug)?; - let mut gs = GrandSlam::new(&client); - gs.get_url_bag() + let client_info = anisette + .get_client_info() .await - .context("Failed to get URL bag from GrandSlam")?; + .context("Failed to get anisette client info")?; + let mut grandslam_client = GrandSlam::new(client_info, debug); + let url_bag = grandslam_client + .get_url_bag() + .await + .context("Failed to get URL bag for login")?; + + let headers = anisette + .get_anisette_headers(&mut grandslam_client) + .await + .context("Failed to get anisette headers for login")?; Ok(AppleAccount { email: email.to_string(), spd: None, - client, anisette, + grandslam_client, }) } - - /// Build a reqwest client with the Apple root certificate - /// - /// # Arguments - /// - `debug`: DANGER, If true, accept invalid certificates and enable verbose connection logging - /// # Errors - /// Returns an error if the reqwest client cannot be built - pub fn build_client(debug: bool) -> Result { - let cert = Certificate::from_der(APPLE_ROOT)?; - let client = ClientBuilder::new() - .add_root_certificate(cert) - .http1_title_case_headers() - .danger_accept_invalid_certs(debug) - .connection_verbose(debug) - .build()?; - - Ok(client) - } } diff --git a/isideload/src/auth/grandslam.rs b/isideload/src/auth/grandslam.rs index 593b5f2..f302f1e 100644 --- a/isideload/src/auth/grandslam.rs +++ b/isideload/src/auth/grandslam.rs @@ -1,64 +1,102 @@ -use crate::SideloadResult as Result; -use log::debug; use plist::Dictionary; -use plist_macro::pretty_print_dictionary; -use reqwest::header::HeaderValue; +use reqwest::{Certificate, ClientBuilder, header::HeaderValue}; +use rootcause::prelude::*; +use tracing::debug; +use crate::anisette::AnisetteClientInfo; + +const APPLE_ROOT: &[u8] = include_bytes!("./apple_root.der"); const URL_BAG: &str = "https://gsa.apple.com/grandslam/GsService2/lookup"; -pub struct GrandSlam<'a> { - client: &'a reqwest::Client, - url_bag: Option, +pub struct GrandSlam { + pub client: reqwest::Client, + pub client_info: AnisetteClientInfo, + pub url_bag: Option, } -impl<'a> GrandSlam<'a> { +impl GrandSlam { /// Create a new GrandSlam instance /// /// # Arguments /// - `client`: The reqwest client to use for requests - pub fn new(client: &'a reqwest::Client) -> Self { + pub fn new(client_info: AnisetteClientInfo, debug: bool) -> Self { Self { - client, + client: Self::build_reqwest_client(debug).unwrap(), + client_info, url_bag: None, } } /// Get the URL bag from GrandSlam - pub async fn get_url_bag(&mut self) -> Result<&Dictionary> { + pub async fn get_url_bag(&mut self) -> Result<&Dictionary, Report> { if self.url_bag.is_none() { debug!("Fetching URL bag from GrandSlam"); let resp = self .client .get(URL_BAG) - .headers(Self::base_headers()) + .headers(self.base_headers()?) .send() - .await? + .await + .context("Failed to fetch URL Bag")? .text() - .await?; - let dict: Dictionary = plist::from_bytes(resp.as_bytes())?; - debug!("{}", pretty_print_dictionary(&dict)); - self.url_bag = Some(dict); + .await + .context("Failed to read URL Bag response text")?; + + let dict: Dictionary = + plist::from_bytes(resp.as_bytes()).context("Failed to parse URL Bag plist")?; + let urls = dict + .get("urls") + .and_then(|v| v.as_dictionary()) + .cloned() + .ok_or_else(|| report!("URL Bag plist missing 'urls' dictionary"))?; + + self.url_bag = Some(urls); } Ok(self.url_bag.as_ref().unwrap()) } - fn base_headers() -> reqwest::header::HeaderMap { + pub fn post(&self, url: &str) -> Result { + let builder = self.client.post(url).headers(self.base_headers()?); + + Ok(builder) + } + + fn base_headers(&self) -> Result { let mut headers = reqwest::header::HeaderMap::new(); - headers.insert("Context-Type", HeaderValue::from_static("text/x-xml-plist")); + headers.insert("Content-Type", HeaderValue::from_static("text/x-xml-plist")); headers.insert("Accept", HeaderValue::from_static("text/x-xml-plist")); headers.insert( "X-Mme-Client-Info", - HeaderValue::from_static( - " ", - ), + HeaderValue::from_str(&self.client_info.client_info)?, + ); + headers.insert( + "User-Agent", + HeaderValue::from_str(&self.client_info.user_agent)?, ); - headers.insert("User-Agent", HeaderValue::from_static("Xcode")); headers.insert("X-Xcode-Version", HeaderValue::from_static("14.2 (14C18)")); headers.insert( "X-Apple-App-Info", HeaderValue::from_static("com.apple.gs.xcode.auth"), ); - headers + Ok(headers) + } + + /// Build a reqwest client with the Apple root certificate + /// + /// # Arguments + /// - `debug`: DANGER, If true, accept invalid certificates and enable verbose connection logging + /// # Errors + /// Returns an error if the reqwest client cannot be built + pub fn build_reqwest_client(debug: bool) -> Result { + let cert = Certificate::from_der(APPLE_ROOT)?; + let client = ClientBuilder::new() + .add_root_certificate(cert) + .http1_title_case_headers() + .danger_accept_invalid_certs(debug) + .connection_verbose(debug) + .build()?; + + Ok(client) } } diff --git a/isideload/src/lib.rs b/isideload/src/lib.rs index 00ca26b..c20b94a 100644 --- a/isideload/src/lib.rs +++ b/isideload/src/lib.rs @@ -1,21 +1,3 @@ -use thiserror::Error as ThisError; -use thiserror_context::{Context, impl_context}; - pub mod anisette; pub mod auth; - -#[derive(Debug, ThisError)] -pub enum ErrorInner { - #[error("Failed sending request: {0}")] - Reqwest(#[from] reqwest::Error), - - #[error("Failed parsing plist: {0}")] - Plist(#[from] plist::Error), - - #[error("Invalid Header: {0}")] - InvalidHeader(#[from] reqwest::header::InvalidHeaderValue), -} - -impl_context!(Error(ErrorInner)); - -pub type SideloadResult = std::result::Result; +pub mod util; diff --git a/isideload/src/util/mod.rs b/isideload/src/util/mod.rs new file mode 100644 index 0000000..088c202 --- /dev/null +++ b/isideload/src/util/mod.rs @@ -0,0 +1 @@ +pub mod plist; diff --git a/isideload/src/util/plist.rs b/isideload/src/util/plist.rs new file mode 100644 index 0000000..a067b5c --- /dev/null +++ b/isideload/src/util/plist.rs @@ -0,0 +1,9 @@ +use plist_macro::{plist_to_xml_bytes, plist_value_to_xml_bytes}; + +pub fn plist_to_xml_string(p: &plist::Dictionary) -> String { + String::from_utf8(plist_to_xml_bytes(p)).unwrap() +} + +pub fn plist_value_to_xml_string(p: &plist::Value) -> String { + String::from_utf8(plist_value_to_xml_bytes(p)).unwrap() +} From 8be330a6be70bfbd1c722d3ea7df2ca6af2f0f2a Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 25 Jan 2026 16:29:07 -0500 Subject: [PATCH 11/71] Logging in (with trusted device 2fa only) --- Cargo.lock | 121 ++++++- examples/minimal/src/main.rs | 8 +- isideload/Cargo.toml | 5 + isideload/src/anisette/mod.rs | 83 ++++- isideload/src/anisette/remote_v3/mod.rs | 225 ++++++------- isideload/src/anisette/remote_v3/state.rs | 2 +- isideload/src/auth/apple_account.rs | 377 ++++++++++++++++++++-- isideload/src/auth/grandslam.rs | 88 ++++- isideload/src/lib.rs | 31 ++ isideload/src/util/plist.rs | 53 ++- 10 files changed, 821 insertions(+), 172 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 188bca2..c7a0bb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,17 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -92,7 +103,16 @@ 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]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array 0.14.7", ] [[package]] @@ -107,6 +127,15 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + [[package]] name = "cc" version = "1.2.53" @@ -150,6 +179,16 @@ dependencies = [ "windows-link", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "cmake" version = "0.1.57" @@ -236,7 +275,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ - "generic-array", + "generic-array 0.14.7", "typenum", ] @@ -263,6 +302,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -397,6 +437,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "generic-array" +version = "1.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf57c49a95fd1fe24b90b3033bee6dc7e8f1288d51494cb44e627c295e38542" +dependencies = [ + "rustversion", + "typenum", +] + [[package]] name = "getrandom" version = "0.2.17" @@ -455,6 +505,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "http" version = "1.4.0" @@ -711,6 +770,16 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "block-padding", + "generic-array 0.14.7", +] + [[package]] name = "ipnet" version = "2.11.0" @@ -731,12 +800,17 @@ dependencies = [ name = "isideload" version = "0.2.0" dependencies = [ + "aes", "async-trait", "base64", + "cbc", "chrono", "futures-util", "hex", + "hmac", "idevice", + "nab138_srp", + "pbkdf2", "plist", "plist-macro", "rand", @@ -872,6 +946,20 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "nab138_srp" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "587a7a2ae38ab9a818f42c12b02a7ad5d738006f78f3b53a9f28da91fe13411d" +dependencies = [ + "base64", + "digest", + "generic-array 1.3.5", + "lazy_static", + "num-bigint", + "subtle", +] + [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -881,12 +969,31 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -908,6 +1015,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + [[package]] name = "percent-encoding" version = "2.3.2" diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index b29fd5a..f170fb8 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -8,8 +8,9 @@ use tracing_subscriber::FmtSubscriber; #[tokio::main] async fn main() { + isideload::init().expect("Failed to initialize error reporting"); let subscriber = FmtSubscriber::builder() - .with_max_level(Level::DEBUG) + .with_max_level(Level::INFO) .finish(); tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); @@ -31,13 +32,12 @@ async fn main() { }; let account = AppleAccountBuilder::new(apple_id) - .danger_debug(true) - .anisette(RemoteV3AnisetteProvider::default().set_serial_number("2".to_string())) + .anisette_provider(RemoteV3AnisetteProvider::default().set_serial_number("2".to_string())) .login(apple_password, get_2fa_code) .await; match account { Ok(_account) => println!("Successfully logged in to Apple ID"), - Err(e) => eprintln!("Failed to log in to Apple ID: {}", e), + Err(e) => eprintln!("Failed to log in to Apple ID: {:?}", e), } } diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 4e924fe..cd52436 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -33,3 +33,8 @@ futures-util = "0.3.31" serde_json = "1.0.149" base64 = "0.22.1" hex = "0.4.3" +srp = { package = "nab138_srp", version = "0.6.0" } +pbkdf2 = "0.12.2" +hmac = "0.12.1" +cbc = { version = "0.1.2", features = ["std"] } +aes = "0.8.4" diff --git a/isideload/src/anisette/mod.rs b/isideload/src/anisette/mod.rs index 196fe25..dd04c94 100644 --- a/isideload/src/anisette/mod.rs +++ b/isideload/src/anisette/mod.rs @@ -1,6 +1,10 @@ pub mod remote_v3; use crate::auth::grandslam::GrandSlam; +use chrono::{DateTime, SubsecRound, Utc}; +use plist::Dictionary; +use plist_macro::plist; +use reqwest::header::HeaderMap; use rootcause::prelude::*; use serde::Deserialize; use std::collections::HashMap; @@ -11,12 +15,83 @@ pub struct AnisetteClientInfo { pub user_agent: String, } +#[derive(Debug, Clone)] +pub struct AnisetteData { + machine_id: String, + one_time_password: String, + routing_info: String, + device_description: String, + device_unique_identifier: String, + local_user_id: String, +} + +impl AnisetteData { + pub fn get_headers(&self, serial: String) -> HashMap { + let dt: DateTime = Utc::now().round_subsecs(0); + + HashMap::from_iter( + [ + ( + "X-Apple-I-Client-Time".to_string(), + dt.format("%+").to_string().replace("+00:00", "Z"), + ), + ("X-Apple-I-SRL-NO".to_string(), serial), + ("X-Apple-I-TimeZone".to_string(), "UTC".to_string()), + ("X-Apple-Locale".to_string(), "en_US".to_string()), + ("X-Apple-I-MD-RINFO".to_string(), self.routing_info.clone()), + ("X-Apple-I-MD-LU".to_string(), self.local_user_id.clone()), + ( + "X-Mme-Device-Id".to_string(), + self.device_unique_identifier.clone(), + ), + ("X-Apple-I-MD".to_string(), self.one_time_password.clone()), + ("X-Apple-I-MD-M".to_string(), self.machine_id.clone()), + ( + "X-Mme-Client-Info".to_string(), + self.device_description.clone(), + ), + ] + .into_iter(), + ) + } + + pub fn get_header_map(&self, serial: String) -> HeaderMap { + let headers_map = self.get_headers(serial); + let mut header_map = HeaderMap::new(); + + for (key, value) in headers_map { + header_map.insert( + reqwest::header::HeaderName::from_bytes(key.as_bytes()).unwrap(), + reqwest::header::HeaderValue::from_str(&value).unwrap(), + ); + } + + header_map + } + + pub fn get_client_provided_data(&self, serial: String) -> Dictionary { + let headers = self.get_headers(serial); + + let mut cpd = plist!(dict { + "bootstrap": "true", + "icscrec": "true", + "loc": "en_US", + "pbe": "false", + "prkgen": "true", + "svct": "iCloud" + }); + + for (key, value) in headers { + cpd.insert(key.to_string(), plist::Value::String(value)); + } + + cpd + } +} + #[async_trait::async_trait] pub trait AnisetteProvider { - async fn get_anisette_headers( - &mut self, - gs: &mut GrandSlam, - ) -> Result, Report>; + async fn get_anisette_data(&mut self, gs: &mut GrandSlam) -> Result; async fn get_client_info(&mut self) -> Result; } diff --git a/isideload/src/anisette/remote_v3/mod.rs b/isideload/src/anisette/remote_v3/mod.rs index bc63071..7f06273 100644 --- a/isideload/src/anisette/remote_v3/mod.rs +++ b/isideload/src/anisette/remote_v3/mod.rs @@ -1,67 +1,25 @@ mod state; -use std::collections::HashMap; use std::fs; use std::path::PathBuf; use base64::prelude::*; -use chrono::{DateTime, SubsecRound, Utc}; +use chrono::{SubsecRound, Utc}; use plist_macro::plist; -use reqwest::header::{HeaderMap, HeaderValue}; +use reqwest::header::{CONTENT_TYPE, HeaderMap, HeaderValue}; use rootcause::prelude::*; use serde::Deserialize; use tokio_tungstenite::tungstenite::Message; use tracing::{debug, info}; use crate::anisette::remote_v3::state::AnisetteState; -use crate::anisette::{AnisetteClientInfo, AnisetteProvider}; +use crate::anisette::{AnisetteClientInfo, AnisetteData, AnisetteProvider}; use crate::auth::grandslam::GrandSlam; -use crate::util::plist::plist_to_xml_string; +use crate::util::plist::PlistDataExtract; use futures_util::{SinkExt, StreamExt}; pub const DEFAULT_ANISETTE_V3_URL: &str = "https://ani.sidestore.io"; -#[derive(Debug)] -pub struct AnisetteData { - machine_id: String, - one_time_password: String, - routing_info: String, - device_description: String, - device_unique_identifier: String, - local_user_id: String, -} - -impl AnisetteData { - pub fn get_headers(&self, serial: String) -> HashMap { - let dt: DateTime = Utc::now().round_subsecs(0); - - HashMap::from_iter( - [ - ( - "X-Apple-I-Client-Time".to_string(), - dt.format("%+").to_string().replace("+00:00", "Z"), - ), - ("X-Apple-I-SRL-NO".to_string(), serial), - ("X-Apple-I-TimeZone".to_string(), "UTC".to_string()), - ("X-Apple-Locale".to_string(), "en_US".to_string()), - ("X-Apple-I-MD-RINFO".to_string(), self.routing_info.clone()), - ("X-Apple-I-MD-LU".to_string(), self.local_user_id.clone()), - ( - "X-Mme-Device-Id".to_string(), - self.device_unique_identifier.clone(), - ), - ("X-Apple-I-MD".to_string(), self.one_time_password.clone()), - ("X-Apple-I-MD-M".to_string(), self.machine_id.clone()), - ( - "X-Mme-Client-Info".to_string(), - self.device_description.clone(), - ), - ] - .into_iter(), - ) - } -} - pub struct RemoteV3AnisetteProvider { pub state: Option, url: String, @@ -117,23 +75,57 @@ impl Default for RemoteV3AnisetteProvider { #[async_trait::async_trait] impl AnisetteProvider for RemoteV3AnisetteProvider { - async fn get_anisette_headers( - &mut self, - gs: &mut GrandSlam, - ) -> Result, Report> { - let state = self.get_state(gs).await?; + async fn get_anisette_data(&mut self, gs: &mut GrandSlam) -> Result { + let state = self.get_state(gs).await?.clone(); + let adi_pb = state + .adi_pb + .as_ref() + .ok_or(report!("Anisette state is not provisioned"))?; + let client_info = self.get_client_info().await?.client_info.clone(); - unimplemented!() + let headers = self + .client + .post(format!("{}/v3/get_headers", self.url)) + .header(CONTENT_TYPE, "application/json") + .body( + serde_json::json!({ + "identifier": BASE64_STANDARD.encode(&state.keychain_identifier), + "adi_pb": BASE64_STANDARD.encode(adi_pb) + }) + .to_string(), + ) + .send() + .await? + .error_for_status()? + .json::() + .await?; + + match headers { + AnisetteHeaders::Headers { + machine_id, + one_time_password, + routing_info, + } => { + let data = AnisetteData { + machine_id, + one_time_password, + routing_info, + device_description: client_info, + device_unique_identifier: state.get_device_id(), + local_user_id: hex::encode(&state.get_md_lu()), + }; + + Ok(data) + } + AnisetteHeaders::GetHeadersError { message } => { + Err(report!("Failed to get anisette headers") + .attach(message) + .into()) + } + } } async fn get_client_info(&mut self) -> Result { - self.ensure_client_info().await?; - Ok(self.client_info.as_ref().unwrap().clone()) - } -} - -impl RemoteV3AnisetteProvider { - async fn ensure_client_info(&mut self) -> Result<(), Report> { if self.client_info.is_none() { let resp = self .client @@ -147,20 +139,20 @@ impl RemoteV3AnisetteProvider { self.client_info = Some(resp); } - debug!("Got client client_info: {:?}", self.client_info); - - Ok(()) + Ok(self.client_info.as_ref().unwrap().clone()) } +} +impl RemoteV3AnisetteProvider { async fn get_state(&mut self, gs: &mut GrandSlam) -> Result<&mut AnisetteState, Report> { let state_path = self.config_path.join("state.plist"); fs::create_dir_all(&self.config_path)?; if self.state.is_none() { if let Ok(state) = plist::from_file(&state_path) { - debug!("Loaded existing anisette state from {:?}", state_path); + info!("Loaded existing anisette state from {:?}", state_path); self.state = Some(state); } else { - debug!("No existing anisette state found"); + info!("No existing anisette state found"); self.state = Some(AnisetteState::new()); } } @@ -177,10 +169,7 @@ impl RemoteV3AnisetteProvider { Ok(state) } - async fn provisioning_headers( - state: &AnisetteState, - gs: &mut GrandSlam, - ) -> Result { + async fn provisioning_headers(state: &AnisetteState) -> Result { let mut headers = HeaderMap::new(); headers.insert( "X-Apple-I-MD-LU", @@ -211,25 +200,14 @@ impl RemoteV3AnisetteProvider { url: &str, ) -> Result<(), Report> { info!("Starting provisioning"); - let urls = gs.get_url_bag().await?; - let start_provisioning = urls - .get("midStartProvisioning") - .and_then(|v| v.as_string()) - .ok_or(report!("Missing URL bag entry for midStartProvisioning"))? - .to_string(); - let end_provisioning = urls - .get("midFinishProvisioning") - .and_then(|v| v.as_string()) - .ok_or(report!("Missing URL bag entry for midFinishProvisioning"))? - .to_string(); + let start_provisioning = gs.get_url("midStartProvisioning").await?; + let end_provisioning = gs.get_url("midFinishProvisioning").await?; let websocket_url = format!("{}/v3/provisioning_session", url) .replace("https://", "wss://") .replace("http://", "ws://"); - let (mut ws_stream, _) = tokio_tungstenite::connect_async(&websocket_url) - .await - .context("Failed to connect anisette provisioning socket")?; + let (mut ws_stream, _) = tokio_tungstenite::connect_async(&websocket_url).await?; loop { let Some(msg) = ws_stream.next().await else { @@ -266,28 +244,18 @@ impl RemoteV3AnisetteProvider { "Request": {} }); - let resp = gs - .post(&start_provisioning)? - .headers(Self::provisioning_headers(state, gs).await?) - .body(plist_to_xml_string(&body)) - .send() + let response = gs + .plist_request( + &start_provisioning, + &body, + Some(Self::provisioning_headers(state).await?), + ) .await - .context("Failed to send start provisioning request")? - .error_for_status() - .context("Start provisioning request returned error")? - .text() - .await - .context("Failed to read start provisioning response text")?; + .context("Failed to send start provisioning request")?; - let resp_plist: plist::Dictionary = plist::from_bytes(resp.as_bytes()) - .context("Failed to parse start provisioning response plist")?; - - let spim = resp_plist - .get("Response") - .and_then(|v| v.as_dictionary()) - .and_then(|d| d.get("spim")) - .and_then(|v| v.as_string()) - .ok_or(report!("Start provisioning response missing spim"))?; + let spim = response + .get_str("spim") + .context("Start provisioning response missing spim")?; ws_stream .send(Message::Text( @@ -308,39 +276,24 @@ impl RemoteV3AnisetteProvider { } }); - let resp = gs - .post(&end_provisioning)? - .headers(Self::provisioning_headers(state, gs).await?) - .body(plist_to_xml_string(&body)) - .send() + let response = gs + .plist_request( + &end_provisioning, + &body, + Some(Self::provisioning_headers(state).await?), + ) .await - .context("Failed to send end provisioning request")? - .error_for_status() - .context("End provisioning request returned error")? - .text() - .await - .context("Failed to read end provisioning response text")?; - - let resp_plist: plist::Dictionary = plist::from_bytes(resp.as_bytes()) - .context("Failed to parse end provisioning response plist")?; - let response = resp_plist - .get("Response") - .and_then(|v| v.as_dictionary()) - .ok_or(report!( - "End provisioning response missing Response dictionary" - ))?; + .context("Failed to send end provisioning request")?; ws_stream .send(Message::Text( serde_json::json!({ "ptm": response - .get("ptm") - .and_then(|v| v.as_string()) - .ok_or(report!("End provisioning response missing ptm"))?, + .get_str("ptm") + .context("End provisioning response missing ptm")?, "tk": response - .get("tk") - .and_then(|v| v.as_string()) - .ok_or(report!("End provisioning response missing tk"))?, + .get_str("tk") + .context("End provisioning response missing tk")?, }) .to_string() .into(), @@ -391,3 +344,19 @@ enum ProvisioningMessage { StartProvisioningError { message: String }, EndProvisioningError { message: String }, } + +#[derive(Deserialize)] +#[serde(tag = "result")] +enum AnisetteHeaders { + GetHeadersError { + message: String, + }, + Headers { + #[serde(rename = "X-Apple-I-MD-M")] + machine_id: String, + #[serde(rename = "X-Apple-I-MD")] + one_time_password: String, + #[serde(rename = "X-Apple-I-MD-RINFO")] + routing_info: String, + }, +} diff --git a/isideload/src/anisette/remote_v3/state.rs b/isideload/src/anisette/remote_v3/state.rs index d87499b..a4aad20 100644 --- a/isideload/src/anisette/remote_v3/state.rs +++ b/isideload/src/anisette/remote_v3/state.rs @@ -37,7 +37,7 @@ where Ok(s.try_into().unwrap()) } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct AnisetteState { #[serde( serialize_with = "bin_serialize", diff --git a/isideload/src/auth/apple_account.rs b/isideload/src/auth/apple_account.rs index 281e820..bd2673f 100644 --- a/isideload/src/auth/apple_account.rs +++ b/isideload/src/auth/apple_account.rs @@ -1,21 +1,48 @@ use crate::{ - anisette::{AnisetteProvider, remote_v3::RemoteV3AnisetteProvider}, - auth::grandslam::GrandSlam, + anisette::{AnisetteData, AnisetteProvider, remote_v3::RemoteV3AnisetteProvider}, + auth::grandslam::{GrandSlam, GrandSlamErrorChecker}, + util::plist::PlistDataExtract, }; +use aes::cipher::block_padding::Pkcs7; +use base64::{Engine, prelude::BASE64_STANDARD}; +use cbc::cipher::{BlockDecryptMut, KeyIvInit}; +use hmac::{Hmac, Mac}; +use plist::Dictionary; +use plist_macro::plist; +use reqwest::header::HeaderMap; use rootcause::prelude::*; -use tracing::{info, warn}; +use sha2::{Digest, Sha256}; +use srp::{ + client::{SrpClient, SrpClientVerifier}, + groups::G_2048, +}; +use tracing::{debug, info, warn}; + +const SERIAL_NUMBER: &str = "2"; pub struct AppleAccount { pub email: String, pub spd: Option, - pub anisette: Box, + pub anisette_provider: Box, + pub anisette_data: AnisetteData, pub grandslam_client: GrandSlam, + login_state: LoginState, + debug: bool, } pub struct AppleAccountBuilder { email: String, debug: Option, - anisette: Option>, + anisette_provider: Option>, +} + +#[derive(Debug)] +pub enum LoginState { + LoggedIn, + NeedsDevice2FA, + NeedsSMS2FA, + NeedsExtraStep(String), + NeedsLogin, } impl AppleAccountBuilder { @@ -27,7 +54,7 @@ impl AppleAccountBuilder { Self { email: email.to_string(), debug: None, - anisette: None, + anisette_provider: None, } } @@ -40,11 +67,24 @@ impl AppleAccountBuilder { self } - pub fn anisette(mut self, anisette: impl AnisetteProvider + 'static) -> Self { - self.anisette = Some(Box::new(anisette)); + pub fn anisette_provider(mut self, anisette_provider: impl AnisetteProvider + 'static) -> Self { + self.anisette_provider = Some(Box::new(anisette_provider)); self } + /// Build the AppleAccount + /// + /// # Errors + /// Returns an error if the reqwest client cannot be built + pub async fn build(self) -> Result { + let debug = self.debug.unwrap_or(false); + let anisette_provider = self + .anisette_provider + .unwrap_or_else(|| Box::new(RemoteV3AnisetteProvider::default())); + + AppleAccount::new(&self.email, anisette_provider, debug).await + } + /// Build the AppleAccount and log in /// /// # Arguments @@ -60,12 +100,9 @@ impl AppleAccountBuilder { where F: Fn() -> Option, { - let debug = self.debug.unwrap_or(false); - let anisette = self - .anisette - .unwrap_or_else(|| Box::new(RemoteV3AnisetteProvider::default())); - - AppleAccount::login(&self.email, password, two_factor_callback, anisette, debug).await + let mut account = self.build().await?; + account.login(password, two_factor_callback).await?; + Ok(account) } } @@ -78,41 +115,321 @@ impl AppleAccount { AppleAccountBuilder::new(email) } - /// Log in to an Apple account with the given email + /// Build the apple account with the given email /// /// Reccomended to use the AppleAccountBuilder instead - pub async fn login( + pub async fn new( email: &str, - password: &str, - two_factor_callback: impl Fn() -> Option, - mut anisette: Box, + mut anisette_provider: Box, debug: bool, ) -> Result { - info!("Logging in to apple ID: {}", email); + info!("Initializing apple account"); if debug { warn!("Debug mode enabled: this is a security risk!"); } - let client_info = anisette + let client_info = anisette_provider .get_client_info() .await .context("Failed to get anisette client info")?; - let mut grandslam_client = GrandSlam::new(client_info, debug); - let url_bag = grandslam_client - .get_url_bag() - .await - .context("Failed to get URL bag for login")?; - let headers = anisette - .get_anisette_headers(&mut grandslam_client) + let mut grandslam_client = GrandSlam::new(client_info, debug); + + let anisette_data = anisette_provider + .get_anisette_data(&mut grandslam_client) .await - .context("Failed to get anisette headers for login")?; + .context("Failed to get anisette data for login")?; Ok(AppleAccount { email: email.to_string(), spd: None, - anisette, + anisette_provider, + anisette_data, grandslam_client, + debug, + login_state: LoginState::NeedsLogin, }) } + + pub async fn login( + &mut self, + password: &str, + two_factor_callback: impl Fn() -> Option, + ) -> Result<(), Report> { + info!("Logging in to apple ID: {}", self.email); + if self.debug { + warn!("Debug mode enabled: this is a security risk!"); + } + + self.login_state = self + .login_inner(password) + .await + .context("Failed to log in to Apple ID")?; + + debug!("Initial login successful"); + + let mut attempts = 0; + + loop { + attempts += 1; + if attempts > 10 { + bail!( + "Couldn't login after 10 attempts, aborting (current state: {:?})", + self.login_state + ); + } + match self.login_state { + LoginState::LoggedIn => { + info!("Successfully logged in to Apple ID"); + return Ok(()); + } + LoginState::NeedsDevice2FA => { + debug!("Trusted device 2FA required"); + let request_code_url = self + .grandslam_client + .get_url("trustedDeviceSecondaryAuth") + .await?; + + let submit_code_url = self.grandslam_client.get_url("validateCode").await?; + + self.grandslam_client + .get(&request_code_url)? + .headers(self.build_2fa_headers().await?) + .send() + .await + .context("Failed to request trusted device 2fa")? + .error_for_status() + .context("Trusted device 2FA request failed")?; + + info!("Trusted device 2FA request sent"); + + let code = two_factor_callback() + .ok_or_else(|| report!("No 2FA code provided, aborting"))?; + + let res = self + .grandslam_client + .get(&submit_code_url)? + .headers(self.build_2fa_headers().await?) + .header("security-code", code) + .send() + .await + .context("Failed to submit trusted device 2fa code")? + .error_for_status() + .context("Trusted device 2FA code submission failed")? + .text() + .await + .context("Failed to read trusted device 2FA response text")?; + + let plist: Dictionary = plist::from_bytes(res.as_bytes()) + .context("Failed to parse trusted device response plist") + .attach_with(|| res.clone())?; + plist + .check_grandslam_error() + .context("Trusted device 2FA rejected")?; + + debug!("Trusted device 2FA completed, need to login again"); + self.login_state = LoginState::NeedsLogin; + } + LoginState::NeedsSMS2FA => { + info!("SMS 2FA required"); + todo!(); + } + LoginState::NeedsExtraStep(_) => todo!(), + LoginState::NeedsLogin => { + debug!("Logging in again..."); + self.login_state = self + .login_inner(password) + .await + .context("Failed to login again")?; + } + } + } + } + + async fn build_2fa_headers(&mut self) -> Result { + let mut headers = self.anisette_data.get_header_map(SERIAL_NUMBER.to_string()); + + let spd = self + .spd + .as_ref() + .ok_or_else(|| report!("SPD data not available, cannot build 2FA headers"))?; + + let adsid = spd + .get_str("adsid") + .context("Failed to build 2FA headers")?; + let token = spd + .get_str("GsIdmsToken") + .context("Failed to build 2FA headers")?; + let identity = BASE64_STANDARD.encode(format!("{}:{}", adsid, token)); + + headers.insert( + "X-Apple-Identity-Token", + reqwest::header::HeaderValue::from_str(&identity)?, + ); + + Ok(headers) + } + + async fn login_inner(&mut self, password: &str) -> Result { + let gs_service_url = self.grandslam_client.get_url("gsService").await?; + + debug!("GrandSlam service URL: {}", gs_service_url); + + let cpd = self + .anisette_data + .get_client_provided_data(SERIAL_NUMBER.to_string()); + + let srp_client = SrpClient::::new(&G_2048); + let a: Vec = (0..32).map(|_| rand::random::()).collect(); + let a_pub = srp_client.compute_public_ephemeral(&a); + + let req1 = plist!(dict { + "Header": { + "Version": "1.0.1" + }, + "Request": { + "A2k": a_pub, // A2k = client public ephemeral + "cpd": cpd.clone(), // cpd = client provided data + "o": "init", // o = operation + "ps": [ // ps = protocols supported + "s2k", + "s2k_fo" + ], + "u": self.email.clone(), // u = username + } + }); + + debug!("Sending initial login request"); + + let response = self + .grandslam_client + .plist_request(&gs_service_url, &req1, None) + .await + .context("Failed to send initial login request")? + .check_grandslam_error() + .context("GrandSlam error during initial login request")?; + + debug!("Login step 1 completed"); + + let salt = response + .get_data("s") + .context("Failed to parse initial login response")?; + let b_pub = response + .get_data("B") + .context("Failed to parse initial login response")?; + let iters = response + .get_signed_integer("i") + .context("Failed to parse initial login response")?; + let c = response + .get_str("c") + .context("Failed to parse initial login response")?; + let selected_protocol = response + .get_str("sp") + .context("Failed to parse initial login response")?; + + debug!( + "Selected SRP protocol: {}, iterations: {}", + selected_protocol, iters + ); + + if selected_protocol != "s2k" && selected_protocol != "s2k_fo" { + bail!("Unsupported SRP protocol selected: {}", selected_protocol); + } + + let hashed_password = Sha256::digest(password.as_bytes()); + + let password_hash = if selected_protocol == "s2k_fo" { + hex::encode(&hashed_password).into_bytes() + } else { + hashed_password.to_vec() + }; + + let mut password_buf = [0u8; 32]; + pbkdf2::pbkdf2::>(&password_hash, salt, iters as u32, &mut password_buf) + .context("Failed to derive password using PBKDF2")?; + + let verifier: SrpClientVerifier = srp_client + .process_reply(&a, &self.email.as_bytes(), &password_buf, salt, b_pub) + .unwrap(); + + let req2 = plist!(dict { + "Header": { + "Version": "1.0.1" + }, + "Request": { + "M1": verifier.proof().to_vec(), // A2k = client public ephemeral + "c": c, // c = client proof from step 1 + "cpd": cpd, // cpd = client provided data + "o": "complete", // o = operation + "u": self.email.clone(), // u = username + } + }); + + debug!("Sending proof login request"); + + let response2 = self + .grandslam_client + .plist_request(&gs_service_url, &req2, None) + .await + .context("Failed to send proof login request")? + .check_grandslam_error() + .context("GrandSlam error during proof login request")?; + + debug!("Login step 2 response received, verifying server proof"); + + let m2 = response2 + .get_data("M2") + .context("Failed to parse proof login response")?; + verifier + .verify_server(m2) + .map_err(|e| report!("Negotiation failed, server proof mismatch: {}", e))?; + + debug!("Server proof verified"); + + let spd_encrypted = response2 + .get_data("spd") + .context("Failed to get SPD from login response")?; + + let spd_decrypted = Self::decrypt_cbc(&verifier, &spd_encrypted) + .context("Failed to decrypt SPD from login response")?; + let spd: plist::Dictionary = + plist::from_bytes(&spd_decrypted).context("Failed to parse decrypted SPD plist")?; + + self.spd = Some(spd); + + let status = response2 + .get_dict("Status") + .context("Failed to parse proof login response")?; + + debug!("Login step 2 completed"); + + if let Some(plist::Value::String(s)) = status.get("au") { + return Ok(match s.as_str() { + "trustedDeviceSecondaryAuth" => LoginState::NeedsDevice2FA, + "secondaryAuth" => LoginState::NeedsSMS2FA, + unknown => LoginState::NeedsExtraStep(unknown.to_string()), + }); + } + + Ok(LoginState::LoggedIn) + } + + fn create_session_key(usr: &SrpClientVerifier, name: &str) -> Result, Report> { + Ok(Hmac::::new_from_slice(&usr.key())? + .chain_update(name.as_bytes()) + .finalize() + .into_bytes() + .to_vec()) + } + + fn decrypt_cbc(usr: &SrpClientVerifier, data: &[u8]) -> Result, Report> { + let extra_data_key = Self::create_session_key(usr, "extra data key:")?; + let extra_data_iv = Self::create_session_key(usr, "extra data iv:")?; + let extra_data_iv = &extra_data_iv[..16]; + + Ok( + cbc::Decryptor::::new_from_slices(&extra_data_key, extra_data_iv)? + .decrypt_padded_vec_mut::(&data)?, + ) + } } diff --git a/isideload/src/auth/grandslam.rs b/isideload/src/auth/grandslam.rs index f302f1e..686da8b 100644 --- a/isideload/src/auth/grandslam.rs +++ b/isideload/src/auth/grandslam.rs @@ -1,9 +1,16 @@ use plist::Dictionary; -use reqwest::{Certificate, ClientBuilder, header::HeaderValue}; +use plist_macro::pretty_print_dictionary; +use reqwest::{ + Certificate, ClientBuilder, + header::{HeaderMap, HeaderValue}, +}; use rootcause::prelude::*; use tracing::debug; -use crate::anisette::AnisetteClientInfo; +use crate::{ + anisette::AnisetteClientInfo, + util::plist::{PlistDataExtract, plist_to_xml_string}, +}; const APPLE_ROOT: &[u8] = include_bytes!("./apple_root.der"); const URL_BAG: &str = "https://gsa.apple.com/grandslam/GsService2/lookup"; @@ -55,12 +62,61 @@ impl GrandSlam { Ok(self.url_bag.as_ref().unwrap()) } + pub async fn get_url(&mut self, key: &str) -> Result { + let url_bag = self.get_url_bag().await?; + let url = url_bag + .get_string(key) + .context("Unable to find key in URL bag")?; + Ok(url) + } + + pub fn get(&self, url: &str) -> Result { + let builder = self.client.get(url).headers(self.base_headers()?); + + Ok(builder) + } + pub fn post(&self, url: &str) -> Result { let builder = self.client.post(url).headers(self.base_headers()?); Ok(builder) } + pub async fn plist_request( + &self, + url: &str, + body: &Dictionary, + additional_headers: Option, + ) -> Result { + let resp = self + .post(url)? + .headers(additional_headers.unwrap_or_else(|| reqwest::header::HeaderMap::new())) + .body(plist_to_xml_string(body)) + .send() + .await + .context("Failed to send grandslam request")? + .error_for_status() + .context("Received error response from grandslam")? + .text() + .await + .context("Failed to read grandslam response as text")?; + + let dict: Dictionary = plist::from_bytes(resp.as_bytes()) + .context("Failed to parse grandslam response plist") + .attach_with(|| resp.clone())?; + + let response_plist = dict + .get("Response") + .and_then(|v| v.as_dictionary()) + .cloned() + .ok_or_else(|| { + report!("grandslam response missing 'Response'") + .attach(pretty_print_dictionary(&dict)) + })?; + + Ok(response_plist) + } + fn base_headers(&self) -> Result { let mut headers = reqwest::header::HeaderMap::new(); headers.insert("Content-Type", HeaderValue::from_static("text/x-xml-plist")); @@ -100,3 +156,31 @@ impl GrandSlam { Ok(client) } } + +pub trait GrandSlamErrorChecker { + fn check_grandslam_error(self) -> Result>; +} + +#[derive(Debug, thiserror::Error)] +pub enum GrandSlamError { + #[error("Auth error {0}: {1}")] + AuthWithMessage(i64, String), +} + +impl GrandSlamErrorChecker for Dictionary { + fn check_grandslam_error(self) -> Result> { + let result = match self.get("Status") { + Some(plist::Value::Dictionary(d)) => d, + _ => &self, + }; + + if result.get_signed_integer("ec").unwrap_or(0) != 0 { + bail!(GrandSlamError::AuthWithMessage( + result.get_signed_integer("ec").unwrap_or(-1), + result.get_str("em").unwrap_or("Unknown error").to_string(), + )) + } + + Ok(self) + } +} diff --git a/isideload/src/lib.rs b/isideload/src/lib.rs index c20b94a..3253576 100644 --- a/isideload/src/lib.rs +++ b/isideload/src/lib.rs @@ -1,3 +1,34 @@ +use rootcause::{ + hooks::{Hooks, context_formatter::ContextFormatterHook}, + prelude::*, +}; + pub mod anisette; pub mod auth; pub mod util; + +struct ReqwestErrorFormatter; + +impl ContextFormatterHook for ReqwestErrorFormatter { + fn display( + &self, + report: rootcause::ReportRef<'_, reqwest::Error, markers::Uncloneable, markers::Local>, + f: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + writeln!(f, "{}", report.format_current_context_unhooked())?; + let mut source = report.current_context_error_source(); + while let Some(s) = source { + writeln!(f, "Caused by: {:?}", s)?; + source = s.source(); + } + Ok(()) + } +} + +pub fn init() -> Result<(), Report> { + Hooks::new() + .context_formatter::(ReqwestErrorFormatter) + .install() + .context("Failed to install error reporting hooks")?; + Ok(()) +} diff --git a/isideload/src/util/plist.rs b/isideload/src/util/plist.rs index a067b5c..b593125 100644 --- a/isideload/src/util/plist.rs +++ b/isideload/src/util/plist.rs @@ -1,4 +1,5 @@ -use plist_macro::{plist_to_xml_bytes, plist_value_to_xml_bytes}; +use plist_macro::{plist_to_xml_bytes, plist_value_to_xml_bytes, pretty_print_dictionary}; +use rootcause::prelude::*; pub fn plist_to_xml_string(p: &plist::Dictionary) -> String { String::from_utf8(plist_to_xml_bytes(p)).unwrap() @@ -7,3 +8,53 @@ pub fn plist_to_xml_string(p: &plist::Dictionary) -> String { pub fn plist_value_to_xml_string(p: &plist::Value) -> String { String::from_utf8(plist_value_to_xml_bytes(p)).unwrap() } + +pub trait PlistDataExtract { + fn get_data(&self, key: &str) -> Result<&[u8], Report>; + fn get_str(&self, key: &str) -> Result<&str, Report>; + fn get_string(&self, key: &str) -> Result; + fn get_signed_integer(&self, key: &str) -> Result; + fn get_dict(&self, key: &str) -> Result<&plist::Dictionary, Report>; +} + +impl PlistDataExtract for plist::Dictionary { + fn get_data(&self, key: &str) -> Result<&[u8], Report> { + self.get(key).and_then(|v| v.as_data()).ok_or_else(|| { + report!("Plist missing data for key '{}'", key).attach(pretty_print_dictionary(self)) + }) + } + + fn get_str(&self, key: &str) -> Result<&str, Report> { + self.get(key).and_then(|v| v.as_string()).ok_or_else(|| { + report!("Plist missing string for key '{}'", key).attach(pretty_print_dictionary(self)) + }) + } + + fn get_string(&self, key: &str) -> Result { + self.get(key) + .and_then(|v| v.as_string()) + .map(|s| s.to_string()) + .ok_or_else(|| { + report!("Plist missing string for key '{}'", key) + .attach(pretty_print_dictionary(self)) + }) + } + + fn get_signed_integer(&self, key: &str) -> Result { + self.get(key) + .and_then(|v| v.as_signed_integer()) + .ok_or_else(|| { + report!("Plist missing signed integer for key '{}'", key) + .attach(pretty_print_dictionary(self)) + }) + } + + fn get_dict(&self, key: &str) -> Result<&plist::Dictionary, Report> { + self.get(key) + .and_then(|v| v.as_dictionary()) + .ok_or_else(|| { + report!("Plist missing dictionary for key '{}'", key) + .attach(pretty_print_dictionary(self)) + }) + } +} From 3031baf9abffdcefd92c3b676d5eff683e861f8e Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 25 Jan 2026 16:57:43 -0500 Subject: [PATCH 12/71] Add SMS 2FA --- examples/minimal/src/main.rs | 16 +-- isideload/src/anisette/remote_v3/mod.rs | 2 +- isideload/src/auth/apple_account.rs | 155 +++++++++++++++++------- isideload/src/auth/grandslam.rs | 20 ++- 4 files changed, 134 insertions(+), 59 deletions(-) diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index f170fb8..d5fb149 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -1,4 +1,4 @@ -use std::{env, path::PathBuf}; +use std::env; use isideload::{ anisette::remote_v3::RemoteV3AnisetteProvider, auth::apple_account::AppleAccountBuilder, @@ -10,19 +10,19 @@ use tracing_subscriber::FmtSubscriber; async fn main() { isideload::init().expect("Failed to initialize error reporting"); let subscriber = FmtSubscriber::builder() - .with_max_level(Level::INFO) + .with_max_level(Level::DEBUG) .finish(); tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); let args: Vec = env::args().collect(); - let _app_path = PathBuf::from( - args.get(1) - .expect("Please provide the path to the app to install"), - ); + // let _app_path = PathBuf::from( + // args.get(1) + // .expect("Please provide the path to the app to install"), + // ); let apple_id = args - .get(2) + .get(1) .expect("Please provide the Apple ID to use for installation"); - let apple_password = args.get(3).expect("Please provide the Apple ID password"); + let apple_password = args.get(2).expect("Please provide the Apple ID password"); let get_2fa_code = || { let mut code = String::new(); diff --git a/isideload/src/anisette/remote_v3/mod.rs b/isideload/src/anisette/remote_v3/mod.rs index 7f06273..e87d3cb 100644 --- a/isideload/src/anisette/remote_v3/mod.rs +++ b/isideload/src/anisette/remote_v3/mod.rs @@ -199,7 +199,7 @@ impl RemoteV3AnisetteProvider { gs: &mut GrandSlam, url: &str, ) -> Result<(), Report> { - info!("Starting provisioning"); + debug!("Starting provisioning"); let start_provisioning = gs.get_url("midStartProvisioning").await?; let end_provisioning = gs.get_url("midFinishProvisioning").await?; diff --git a/isideload/src/auth/apple_account.rs b/isideload/src/auth/apple_account.rs index bd2673f..1538908 100644 --- a/isideload/src/auth/apple_account.rs +++ b/isideload/src/auth/apple_account.rs @@ -156,7 +156,7 @@ impl AppleAccount { password: &str, two_factor_callback: impl Fn() -> Option, ) -> Result<(), Report> { - info!("Logging in to apple ID: {}", self.email); + info!("Logging in to Apple ID: {}", self.email); if self.debug { warn!("Debug mode enabled: this is a security risk!"); } @@ -184,55 +184,19 @@ impl AppleAccount { return Ok(()); } LoginState::NeedsDevice2FA => { - debug!("Trusted device 2FA required"); - let request_code_url = self - .grandslam_client - .get_url("trustedDeviceSecondaryAuth") - .await?; - - let submit_code_url = self.grandslam_client.get_url("validateCode").await?; - - self.grandslam_client - .get(&request_code_url)? - .headers(self.build_2fa_headers().await?) - .send() + self.trusted_device_2fa(&two_factor_callback) .await - .context("Failed to request trusted device 2fa")? - .error_for_status() - .context("Trusted device 2FA request failed")?; - - info!("Trusted device 2FA request sent"); - - let code = two_factor_callback() - .ok_or_else(|| report!("No 2FA code provided, aborting"))?; - - let res = self - .grandslam_client - .get(&submit_code_url)? - .headers(self.build_2fa_headers().await?) - .header("security-code", code) - .send() - .await - .context("Failed to submit trusted device 2fa code")? - .error_for_status() - .context("Trusted device 2FA code submission failed")? - .text() - .await - .context("Failed to read trusted device 2FA response text")?; - - let plist: Dictionary = plist::from_bytes(res.as_bytes()) - .context("Failed to parse trusted device response plist") - .attach_with(|| res.clone())?; - plist - .check_grandslam_error() - .context("Trusted device 2FA rejected")?; - + .context("Failed to complete trusted device 2FA")?; debug!("Trusted device 2FA completed, need to login again"); self.login_state = LoginState::NeedsLogin; } LoginState::NeedsSMS2FA => { info!("SMS 2FA required"); - todo!(); + self.sms_2fa(&two_factor_callback) + .await + .context("Failed to complete SMS 2FA")?; + debug!("SMS 2FA completed, need to login again"); + self.login_state = LoginState::NeedsLogin; } LoginState::NeedsExtraStep(_) => todo!(), LoginState::NeedsLogin => { @@ -246,6 +210,108 @@ impl AppleAccount { } } + async fn trusted_device_2fa( + &mut self, + two_factor_callback: impl Fn() -> Option, + ) -> Result<(), Report> { + debug!("Trusted device 2FA required"); + let request_code_url = self + .grandslam_client + .get_url("trustedDeviceSecondaryAuth") + .await?; + + let submit_code_url = self.grandslam_client.get_url("validateCode").await?; + + self.grandslam_client + .get(&request_code_url)? + .headers(self.build_2fa_headers().await?) + .send() + .await + .context("Failed to request trusted device 2fa")? + .error_for_status() + .context("Trusted device 2FA request failed")?; + + info!("Trusted device 2FA request sent"); + + let code = + two_factor_callback().ok_or_else(|| report!("No 2FA code provided, aborting"))?; + + let res = self + .grandslam_client + .get(&submit_code_url)? + .headers(self.build_2fa_headers().await?) + .header("security-code", code) + .send() + .await + .context("Failed to submit trusted device 2fa code")? + .error_for_status() + .context("Trusted device 2FA code submission failed")? + .text() + .await + .context("Failed to read trusted device 2FA response text")?; + + let plist: Dictionary = plist::from_bytes(res.as_bytes()) + .context("Failed to parse trusted device response plist") + .attach_with(|| res.clone())?; + plist + .check_grandslam_error() + .context("Trusted device 2FA rejected")?; + + Ok(()) + } + + async fn sms_2fa( + &mut self, + two_factor_callback: impl Fn() -> Option, + ) -> Result<(), Report> { + debug!("SMS 2FA required"); + let request_code_url = self.grandslam_client.get_url("secondaryAuth").await?; + + self.grandslam_client + .get_sms(&request_code_url)? + .headers(self.build_2fa_headers().await?) + .send() + .await + .context("Failed to request SMS 2fa")? + .error_for_status() + .context("SMS 2FA request failed")?; + + info!("SMS 2FA request sent"); + + let code = + two_factor_callback().ok_or_else(|| report!("No 2FA code provided, aborting"))?; + + let body = serde_json::json!({ + "securityCode": { + "code": code + }, + "phoneNumber": { + "id": "1" + }, + "mode": "sms" + }); + + let res = self + .grandslam_client + .post("https://gsa.apple.com/auth/verify/phone/securitycode")? + .headers(self.build_2fa_headers().await?) + .header("Content-Type", "application/json") + .header("Accept", "application/json") + .body(body.to_string()) + .send() + .await + .context("Failed to submit SMS 2fa code")? + .error_for_status() + .context("SMS 2FA code submission failed")? + .text() + .await + .context("Failed to read SMS 2FA response text")?; + + debug!("SMS 2FA response: {}", res); + + Ok(()) + } + async fn build_2fa_headers(&mut self) -> Result { let mut headers = self.anisette_data.get_header_map(SERIAL_NUMBER.to_string()); @@ -407,6 +473,7 @@ impl AppleAccount { return Ok(match s.as_str() { "trustedDeviceSecondaryAuth" => LoginState::NeedsDevice2FA, "secondaryAuth" => LoginState::NeedsSMS2FA, + "repair" => LoginState::LoggedIn, // Just means that you don't have 2FA set up unknown => LoginState::NeedsExtraStep(unknown.to_string()), }); } diff --git a/isideload/src/auth/grandslam.rs b/isideload/src/auth/grandslam.rs index 686da8b..718c617 100644 --- a/isideload/src/auth/grandslam.rs +++ b/isideload/src/auth/grandslam.rs @@ -41,7 +41,7 @@ impl GrandSlam { let resp = self .client .get(URL_BAG) - .headers(self.base_headers()?) + .headers(self.base_headers(false)?) .send() .await .context("Failed to fetch URL Bag")? @@ -71,13 +71,19 @@ impl GrandSlam { } pub fn get(&self, url: &str) -> Result { - let builder = self.client.get(url).headers(self.base_headers()?); + let builder = self.client.get(url).headers(self.base_headers(false)?); + + Ok(builder) + } + + pub fn get_sms(&self, url: &str) -> Result { + let builder = self.client.get(url).headers(self.base_headers(true)?); Ok(builder) } pub fn post(&self, url: &str) -> Result { - let builder = self.client.post(url).headers(self.base_headers()?); + let builder = self.client.post(url).headers(self.base_headers(false)?); Ok(builder) } @@ -117,10 +123,12 @@ impl GrandSlam { Ok(response_plist) } - fn base_headers(&self) -> Result { + fn base_headers(&self, sms: bool) -> Result { let mut headers = reqwest::header::HeaderMap::new(); - headers.insert("Content-Type", HeaderValue::from_static("text/x-xml-plist")); - headers.insert("Accept", HeaderValue::from_static("text/x-xml-plist")); + if !sms { + headers.insert("Content-Type", HeaderValue::from_static("text/x-xml-plist")); + headers.insert("Accept", HeaderValue::from_static("text/x-xml-plist")); + } headers.insert( "X-Mme-Client-Info", HeaderValue::from_str(&self.client_info.client_info)?, From 3e67d93bd8ba30a38a8c47803fb26133a266f6a8 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 25 Jan 2026 16:57:51 -0500 Subject: [PATCH 13/71] Add github action to build example --- .github/workflows/build.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2dde64a..8e4bbe5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,8 +16,14 @@ jobs: matrix: include: - platform: "ubuntu-latest" + artifact_name: "minimal" + asset_name: "minimal-linux" - platform: "windows-latest" + artifact_name: "minimal.exe" + asset_name: "minimal-windows.exe" - platform: "macos-latest" + artifact_name: "minimal" + asset_name: "minimal-macos" runs-on: ${{ matrix.platform }} steps: @@ -38,4 +44,10 @@ jobs: uses: dtolnay/rust-toolchain@stable - name: Build - run: cargo build + run: cargo build --p minimal + + - name: Upload binary + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.asset_name }} + path: target/release/examples/${{ matrix.artifact_name }} From 7029905c1cd8396efa6863b1a4384de21d96112c Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 25 Jan 2026 16:58:01 -0500 Subject: [PATCH 14/71] Fix action --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8e4bbe5..90e34d1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,7 +44,7 @@ jobs: uses: dtolnay/rust-toolchain@stable - name: Build - run: cargo build --p minimal + run: cargo build -p minimal - name: Upload binary uses: actions/upload-artifact@v4 From 97f6f3996765f3636b905312ed8377a468df5f6d Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 25 Jan 2026 16:59:43 -0500 Subject: [PATCH 15/71] Upload the correct artifact --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 90e34d1..59a7122 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,4 +50,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: ${{ matrix.asset_name }} - path: target/release/examples/${{ matrix.artifact_name }} + path: target/debug/${{ matrix.artifact_name }} From aff3c73219d8bc8db08c5dda962551ebd052597e Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 25 Jan 2026 17:34:20 -0500 Subject: [PATCH 16/71] force sms 2fa for testing --- isideload/src/auth/apple_account.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isideload/src/auth/apple_account.rs b/isideload/src/auth/apple_account.rs index 1538908..ddaff81 100644 --- a/isideload/src/auth/apple_account.rs +++ b/isideload/src/auth/apple_account.rs @@ -471,7 +471,7 @@ impl AppleAccount { if let Some(plist::Value::String(s)) = status.get("au") { return Ok(match s.as_str() { - "trustedDeviceSecondaryAuth" => LoginState::NeedsDevice2FA, + "trustedDeviceSecondaryAuth" => LoginState::NeedsSMS2FA, "secondaryAuth" => LoginState::NeedsSMS2FA, "repair" => LoginState::LoggedIn, // Just means that you don't have 2FA set up unknown => LoginState::NeedsExtraStep(unknown.to_string()), From 33cd3d10a467be147a4a26bff7d3a1da1a262812 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 25 Jan 2026 23:26:30 -0500 Subject: [PATCH 17/71] Add extra auth step to login --- examples/minimal/src/main.rs | 2 +- isideload/src/auth/apple_account.rs | 46 +++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index d5fb149..d36dc2f 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -37,7 +37,7 @@ async fn main() { .await; match account { - Ok(_account) => println!("Successfully logged in to Apple ID"), + Ok(a) => println!("Logged in. {}", a), Err(e) => eprintln!("Failed to log in to Apple ID: {:?}", e), } } diff --git a/isideload/src/auth/apple_account.rs b/isideload/src/auth/apple_account.rs index ddaff81..6e8782d 100644 --- a/isideload/src/auth/apple_account.rs +++ b/isideload/src/auth/apple_account.rs @@ -178,7 +178,7 @@ impl AppleAccount { self.login_state ); } - match self.login_state { + match &self.login_state { LoginState::LoggedIn => { info!("Successfully logged in to Apple ID"); return Ok(()); @@ -198,7 +198,13 @@ impl AppleAccount { debug!("SMS 2FA completed, need to login again"); self.login_state = LoginState::NeedsLogin; } - LoginState::NeedsExtraStep(_) => todo!(), + LoginState::NeedsExtraStep(s) => { + info!("Additional authentication step required: {}", s); + if self.get_pet().is_err() { + bail!("Additional authentication required: {}", s); + } + self.login_state = LoginState::LoggedIn; + } LoginState::NeedsLogin => { debug!("Logging in again..."); self.login_state = self @@ -210,6 +216,29 @@ impl AppleAccount { } } + pub fn get_name(&self) -> Result<(String, String), Report> { + let spd = self + .spd + .as_ref() + .ok_or_else(|| report!("SPD not available, cannot get name"))?; + + Ok((spd.get_string("fn")?, spd.get_string("ln")?)) + } + + fn get_pet(&self) -> Result { + let spd = self + .spd + .as_ref() + .ok_or_else(|| report!("SPD not available, cannot get pet"))?; + + let pet = spd + .get_dict("t")? + .get_dict("com.apple.gs.idms.pet")? + .get_string("token")?; + + Ok(pet) + } + async fn trusted_device_2fa( &mut self, two_factor_callback: impl Fn() -> Option, @@ -471,7 +500,7 @@ impl AppleAccount { if let Some(plist::Value::String(s)) = status.get("au") { return Ok(match s.as_str() { - "trustedDeviceSecondaryAuth" => LoginState::NeedsSMS2FA, + "trustedDeviceSecondaryAuth" => LoginState::NeedsDevice2FA, "secondaryAuth" => LoginState::NeedsSMS2FA, "repair" => LoginState::LoggedIn, // Just means that you don't have 2FA set up unknown => LoginState::NeedsExtraStep(unknown.to_string()), @@ -500,3 +529,14 @@ impl AppleAccount { ) } } + +impl std::fmt::Display for AppleAccount { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Apple Account: ")?; + match self.get_name() { + Ok((first, last)) => write!(f, "{} {} ", first, last), + Err(_) => Ok(()), + }?; + write!(f, "{} ({:?})", self.email, self.login_state) + } +} From 2167300f98c9be452010246e5f134617260fc105 Mon Sep 17 00:00:00 2001 From: nab138 Date: Mon, 26 Jan 2026 11:45:05 -0500 Subject: [PATCH 18/71] Fix SMS 2FA headers --- isideload/src/auth/apple_account.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/isideload/src/auth/apple_account.rs b/isideload/src/auth/apple_account.rs index 6e8782d..e2c08ac 100644 --- a/isideload/src/auth/apple_account.rs +++ b/isideload/src/auth/apple_account.rs @@ -9,7 +9,7 @@ use cbc::cipher::{BlockDecryptMut, KeyIvInit}; use hmac::{Hmac, Mac}; use plist::Dictionary; use plist_macro::plist; -use reqwest::header::HeaderMap; +use reqwest::header::{HeaderMap, HeaderValue}; use rootcause::prelude::*; use sha2::{Digest, Sha256}; use srp::{ @@ -294,6 +294,7 @@ impl AppleAccount { two_factor_callback: impl Fn() -> Option, ) -> Result<(), Report> { debug!("SMS 2FA required"); + let request_code_url = self.grandslam_client.get_url("secondaryAuth").await?; self.grandslam_client @@ -301,7 +302,7 @@ impl AppleAccount { .headers(self.build_2fa_headers().await?) .send() .await - .context("Failed to request SMS 2fa")? + .context("Failed to request SMS 2FA")? .error_for_status() .context("SMS 2FA request failed")?; @@ -315,26 +316,33 @@ impl AppleAccount { "code": code }, "phoneNumber": { - "id": "1" + "id": 1 }, "mode": "sms" }); + debug!("{}", body); + + let mut headers = self.build_2fa_headers().await?; + headers.insert("Content-Type", HeaderValue::from_static("application/json")); + headers.insert( + "Accept", + HeaderValue::from_static("application/json, text/javascript, */*; q=0.01"), + ); + let res = self .grandslam_client .post("https://gsa.apple.com/auth/verify/phone/securitycode")? - .headers(self.build_2fa_headers().await?) - .header("Content-Type", "application/json") - .header("Accept", "application/json") + .headers(headers) .body(body.to_string()) .send() .await - .context("Failed to submit SMS 2fa code")? + .context("Failed to submit SMS 2FA code")? .error_for_status() .context("SMS 2FA code submission failed")? .text() .await - .context("Failed to read SMS 2FA response text")?; + .context("Failed to read SMS 2FA response")?; debug!("SMS 2FA response: {}", res); From 846b6cde05a036f1be64cc85933b8b631eb21de6 Mon Sep 17 00:00:00 2001 From: nab138 Date: Mon, 26 Jan 2026 14:48:59 -0500 Subject: [PATCH 19/71] Add comments, reduce unnceccsary headers, better sms error handling --- isideload/src/anisette/mod.rs | 30 ++++++------ isideload/src/anisette/remote_v3/mod.rs | 24 ++++----- isideload/src/auth/apple_account.rs | 65 +++++++++++++++++++++---- isideload/src/auth/grandslam.rs | 18 +++---- 4 files changed, 91 insertions(+), 46 deletions(-) diff --git a/isideload/src/anisette/mod.rs b/isideload/src/anisette/mod.rs index dd04c94..1894915 100644 --- a/isideload/src/anisette/mod.rs +++ b/isideload/src/anisette/mod.rs @@ -19,7 +19,7 @@ pub struct AnisetteClientInfo { pub struct AnisetteData { machine_id: String, one_time_password: String, - routing_info: String, + pub routing_info: String, device_description: String, device_unique_identifier: String, local_user_id: String, @@ -27,29 +27,29 @@ pub struct AnisetteData { impl AnisetteData { pub fn get_headers(&self, serial: String) -> HashMap { - let dt: DateTime = Utc::now().round_subsecs(0); + // let dt: DateTime = Utc::now().round_subsecs(0); HashMap::from_iter( [ - ( - "X-Apple-I-Client-Time".to_string(), - dt.format("%+").to_string().replace("+00:00", "Z"), - ), - ("X-Apple-I-SRL-NO".to_string(), serial), - ("X-Apple-I-TimeZone".to_string(), "UTC".to_string()), - ("X-Apple-Locale".to_string(), "en_US".to_string()), - ("X-Apple-I-MD-RINFO".to_string(), self.routing_info.clone()), - ("X-Apple-I-MD-LU".to_string(), self.local_user_id.clone()), + // ( + // "X-Apple-I-Client-Time".to_string(), + // dt.format("%+").to_string().replace("+00:00", "Z"), + // ), + // ("X-Apple-I-SRL-NO".to_string(), serial), + // ("X-Apple-I-TimeZone".to_string(), "UTC".to_string()), + // ("X-Apple-Locale".to_string(), "en_US".to_string()), + // ("X-Apple-I-MD-RINFO".to_string(), self.routing_info.clone()), + // ("X-Apple-I-MD-LU".to_string(), self.local_user_id.clone()), ( "X-Mme-Device-Id".to_string(), self.device_unique_identifier.clone(), ), ("X-Apple-I-MD".to_string(), self.one_time_password.clone()), ("X-Apple-I-MD-M".to_string(), self.machine_id.clone()), - ( - "X-Mme-Client-Info".to_string(), - self.device_description.clone(), - ), + // ( + // "X-Mme-Client-Info".to_string(), + // self.device_description.clone(), + // ), ] .into_iter(), ) diff --git a/isideload/src/anisette/remote_v3/mod.rs b/isideload/src/anisette/remote_v3/mod.rs index e87d3cb..ecbe913 100644 --- a/isideload/src/anisette/remote_v3/mod.rs +++ b/isideload/src/anisette/remote_v3/mod.rs @@ -175,18 +175,18 @@ impl RemoteV3AnisetteProvider { "X-Apple-I-MD-LU", HeaderValue::from_str(&hex::encode(state.get_md_lu()))?, ); - headers.insert( - "X-Apple-I-Client-Time", - HeaderValue::from_str( - &Utc::now() - .round_subsecs(0) - .format("%+") - .to_string() - .replace("+00:00", "Z"), - )?, - ); - headers.insert("X-Apple-I-TimeZone", HeaderValue::from_static("UTC")); - headers.insert("X-Apple-Locale", HeaderValue::from_static("en_US")); + // headers.insert( + // "X-Apple-I-Client-Time", + // HeaderValue::from_str( + // &Utc::now() + // .round_subsecs(0) + // .format("%+") + // .to_string() + // .replace("+00:00", "Z"), + // )?, + // ); + // headers.insert("X-Apple-I-TimeZone", HeaderValue::from_static("UTC")); + // headers.insert("X-Apple-Locale", HeaderValue::from_static("en_US")); headers.insert( "X-Mme-Device-Id", HeaderValue::from_str(&state.get_device_id())?, diff --git a/isideload/src/auth/apple_account.rs b/isideload/src/auth/apple_account.rs index e2c08ac..8a75afe 100644 --- a/isideload/src/auth/apple_account.rs +++ b/isideload/src/auth/apple_account.rs @@ -72,7 +72,7 @@ impl AppleAccountBuilder { self } - /// Build the AppleAccount + /// Build the AppleAccount without logging in /// /// # Errors /// Returns an error if the reqwest client cannot be built @@ -118,6 +118,10 @@ impl AppleAccount { /// Build the apple account with the given email /// /// Reccomended to use the AppleAccountBuilder instead + /// # Arguments + /// - `email`: The Apple ID email address + /// - `anisette_provider`: The anisette provider to use + /// - `debug`: DANGER, If true, accept invalid certificates and enable verbose connection pub async fn new( email: &str, mut anisette_provider: Box, @@ -151,6 +155,12 @@ impl AppleAccount { }) } + /// Log in to the Apple ID account + /// # Arguments + /// - `password`: The Apple ID password + /// - `two_factor_callback`: A callback function that returns the two-factor authentication code + /// # Errors + /// Returns an error if the login fails pub async fn login( &mut self, password: &str, @@ -216,6 +226,7 @@ impl AppleAccount { } } + /// Get the user's first and last name associated with the Apple ID pub fn get_name(&self) -> Result<(String, String), Report> { let spd = self .spd @@ -269,6 +280,10 @@ impl AppleAccount { .grandslam_client .get(&submit_code_url)? .headers(self.build_2fa_headers().await?) + .header( + "X-Apple-I-MD-RINFO", + self.anisette_data.routing_info.clone(), + ) .header("security-code", code) .send() .await @@ -321,8 +336,6 @@ impl AppleAccount { "mode": "sms" }); - debug!("{}", body); - let mut headers = self.build_2fa_headers().await?; headers.insert("Content-Type", HeaderValue::from_static("application/json")); headers.insert( @@ -337,14 +350,46 @@ impl AppleAccount { .body(body.to_string()) .send() .await - .context("Failed to submit SMS 2FA code")? - .error_for_status() - .context("SMS 2FA code submission failed")? - .text() - .await - .context("Failed to read SMS 2FA response")?; + .context("Failed to submit SMS 2FA code")?; - debug!("SMS 2FA response: {}", res); + if !res.status().is_success() { + let status = res.status(); + let text = res + .text() + .await + .context("Failed to read SMS 2FA error response text")?; + // try to parse as json, if it fails, just bail with the text + if let Ok(json) = serde_json::from_str::(&text) { + if let Some(service_errors) = json.get("serviceErrors") { + if let Some(first_error) = service_errors.as_array().and_then(|arr| arr.get(0)) + { + let code = first_error + .get("code") + .and_then(|c| c.as_str()) + .unwrap_or("unknown"); + let title = first_error + .get("title") + .and_then(|t| t.as_str()) + .unwrap_or("No title provided"); + let message = first_error + .get("message") + .and_then(|m| m.as_str()) + .unwrap_or("No message provided"); + bail!( + "SMS 2FA code submission failed (code {}): {} - {}", + code, + title, + message + ); + } + } + } + bail!( + "SMS 2FA code submission failed with http status {}: {}", + status, + text + ); + }; Ok(()) } diff --git a/isideload/src/auth/grandslam.rs b/isideload/src/auth/grandslam.rs index 718c617..ee0f4cf 100644 --- a/isideload/src/auth/grandslam.rs +++ b/isideload/src/auth/grandslam.rs @@ -133,15 +133,15 @@ impl GrandSlam { "X-Mme-Client-Info", HeaderValue::from_str(&self.client_info.client_info)?, ); - headers.insert( - "User-Agent", - HeaderValue::from_str(&self.client_info.user_agent)?, - ); - headers.insert("X-Xcode-Version", HeaderValue::from_static("14.2 (14C18)")); - headers.insert( - "X-Apple-App-Info", - HeaderValue::from_static("com.apple.gs.xcode.auth"), - ); + // headers.insert( + // "User-Agent", + // HeaderValue::from_str(&self.client_info.user_agent)?, + // ); + // headers.insert("X-Xcode-Version", HeaderValue::from_static("14.2 (14C18)")); + // headers.insert( + // "X-Apple-App-Info", + // HeaderValue::from_static("com.apple.gs.xcode.auth"), + // ); Ok(headers) } From f1790cec3dd0f0fba6423d1f38b517a77151fd3f Mon Sep 17 00:00:00 2001 From: nab138 Date: Mon, 26 Jan 2026 21:42:52 -0500 Subject: [PATCH 20/71] Fix SMS 2FA --- isideload/src/auth/apple_account.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/isideload/src/auth/apple_account.rs b/isideload/src/auth/apple_account.rs index 8a75afe..8326158 100644 --- a/isideload/src/auth/apple_account.rs +++ b/isideload/src/auth/apple_account.rs @@ -280,10 +280,6 @@ impl AppleAccount { .grandslam_client .get(&submit_code_url)? .headers(self.build_2fa_headers().await?) - .header( - "X-Apple-I-MD-RINFO", - self.anisette_data.routing_info.clone(), - ) .header("security-code", code) .send() .await @@ -414,6 +410,10 @@ impl AppleAccount { "X-Apple-Identity-Token", reqwest::header::HeaderValue::from_str(&identity)?, ); + headers.insert( + "X-Apple-I-MD-RINFO", + reqwest::header::HeaderValue::from_str(&self.anisette_data.routing_info)?, + ); Ok(headers) } From 3b3f63131569ea8f5de6825a5a6dda2acfc7efec Mon Sep 17 00:00:00 2001 From: nab138 Date: Mon, 26 Jan 2026 22:24:57 -0500 Subject: [PATCH 21/71] Impliment getting app token --- Cargo.lock | 87 ++++++++++++++- examples/minimal/Cargo.toml | 1 + examples/minimal/src/main.rs | 10 +- isideload/Cargo.toml | 1 + isideload/src/anisette/remote_v3/mod.rs | 2 +- isideload/src/auth/apple_account.rs | 134 ++++++++++++++++++++++-- isideload/src/auth/grandslam.rs | 13 +-- isideload/src/lib.rs | 9 ++ isideload/src/util/plist.rs | 54 ++++++++-- 9 files changed, 283 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c7a0bb1..bc9f3a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,16 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array 0.14.7", +] + [[package]] name = "aes" version = "0.8.4" @@ -19,6 +29,20 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -276,9 +300,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array 0.14.7", + "rand_core 0.6.4", "typenum", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "data-encoding" version = "2.10.0" @@ -474,6 +508,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "h2" version = "0.4.13" @@ -801,6 +845,7 @@ name = "isideload" version = "0.2.0" dependencies = [ "aes", + "aes-gcm", "async-trait", "base64", "cbc", @@ -920,6 +965,7 @@ name = "minimal" version = "0.1.0" dependencies = [ "isideload", + "plist-macro", "tokio", "tracing", "tracing-subscriber", @@ -1009,6 +1055,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "openssl-probe" version = "0.2.1" @@ -1065,6 +1117,18 @@ dependencies = [ "plist", ] +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -1185,7 +1249,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha", - "rand_core", + "rand_core 0.9.5", ] [[package]] @@ -1195,7 +1259,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", ] [[package]] @@ -1903,6 +1976,16 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "untrusted" version = "0.9.0" diff --git a/examples/minimal/Cargo.toml b/examples/minimal/Cargo.toml index a96136b..cdafa1f 100644 --- a/examples/minimal/Cargo.toml +++ b/examples/minimal/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] isideload = { path = "../../isideload" } +plist-macro = "0.1.3" tokio = { version = "1.49.0", features = ["rt-multi-thread", "macros"] } tracing = "0.1.44" tracing-subscriber = "0.3.22" diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index d36dc2f..7d5c170 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -3,6 +3,7 @@ use std::env; use isideload::{ anisette::remote_v3::RemoteV3AnisetteProvider, auth::apple_account::AppleAccountBuilder, }; +use plist_macro::pretty_print_dictionary; use tracing::Level; use tracing_subscriber::FmtSubscriber; @@ -36,8 +37,15 @@ async fn main() { .login(apple_password, get_2fa_code) .await; - match account { + match &account { Ok(a) => println!("Logged in. {}", a), Err(e) => eprintln!("Failed to log in to Apple ID: {:?}", e), } + + let app_token = account.unwrap().get_app_token("xcode.auth").await; + + match app_token { + Ok(t) => println!("App token: {}", pretty_print_dictionary(&t)), + Err(e) => eprintln!("Failed to get app token: {:?}", e), + } } diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index cd52436..b200d13 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -38,3 +38,4 @@ pbkdf2 = "0.12.2" hmac = "0.12.1" cbc = { version = "0.1.2", features = ["std"] } aes = "0.8.4" +aes-gcm = "0.10.3" diff --git a/isideload/src/anisette/remote_v3/mod.rs b/isideload/src/anisette/remote_v3/mod.rs index ecbe913..d292734 100644 --- a/isideload/src/anisette/remote_v3/mod.rs +++ b/isideload/src/anisette/remote_v3/mod.rs @@ -4,7 +4,7 @@ use std::fs; use std::path::PathBuf; use base64::prelude::*; -use chrono::{SubsecRound, Utc}; +// use chrono::{SubsecRound, Utc}; use plist_macro::plist; use reqwest::header::{CONTENT_TYPE, HeaderMap, HeaderValue}; use rootcause::prelude::*; diff --git a/isideload/src/auth/apple_account.rs b/isideload/src/auth/apple_account.rs index 8326158..09b6def 100644 --- a/isideload/src/auth/apple_account.rs +++ b/isideload/src/auth/apple_account.rs @@ -3,10 +3,14 @@ use crate::{ auth::grandslam::{GrandSlam, GrandSlamErrorChecker}, util::plist::PlistDataExtract, }; -use aes::cipher::block_padding::Pkcs7; +use aes::{ + Aes256, + cipher::{block_padding::Pkcs7, consts::U16}, +}; +use aes_gcm::{AeadInPlace, AesGcm, KeyInit, Nonce}; use base64::{Engine, prelude::BASE64_STANDARD}; use cbc::cipher::{BlockDecryptMut, KeyIvInit}; -use hmac::{Hmac, Mac}; +use hmac::Mac; use plist::Dictionary; use plist_macro::plist; use reqwest::header::{HeaderMap, HeaderValue}; @@ -563,12 +567,93 @@ impl AppleAccount { Ok(LoginState::LoggedIn) } - fn create_session_key(usr: &SrpClientVerifier, name: &str) -> Result, Report> { - Ok(Hmac::::new_from_slice(&usr.key())? - .chain_update(name.as_bytes()) + pub async fn get_app_token(&mut self, app: &str) -> Result { + let app = if app.contains("com.apple.gs.") { + app.to_string() + } else { + format!("com.apple.gs.{}", app) + }; + + let spd = self + .spd + .as_ref() + .ok_or_else(|| report!("SPD data not available, cannot get app token"))?; + + let dsid = spd.get_str("adsid").context("Failed to get app token")?; + let auth_token = spd + .get_str("GsIdmsToken") + .context("Failed to get app token")?; + let session_key = spd.get_data("sk").context("Failed to get app token")?; + let c = spd.get_data("c").context("Failed to get app token")?; + + let checksum = as hmac::Mac>::new_from_slice(session_key) + .unwrap() + .chain_update("apptokens".as_bytes()) + .chain_update(dsid.as_bytes()) + .chain_update(app.as_bytes()) .finalize() .into_bytes() - .to_vec()) + .to_vec(); + + let gs_service_url = self.grandslam_client.get_url("gsService").await?; + let cpd = self + .anisette_data + .get_client_provided_data(SERIAL_NUMBER.to_string()); + + let request = plist!(dict { + "Header": { + "Version": "1.0.1" + }, + "Request": { + "app": [app], + "c": c, + "checksum": checksum, + "cpd": cpd, + "o": "apptokens", + "u": dsid, + "t": auth_token + } + }); + + let resp = self + .grandslam_client + .plist_request(&gs_service_url, &request, None) + .await + .context("Failed to send app token request")? + .check_grandslam_error() + .context("GrandSlam error during app token request")?; + + let encrypted_token = resp + .get_data("et") + .context("Failed to get encrypted token")?; + + let decrypted_token = Self::decrypt_gcm(&encrypted_token, &session_key) + .context("Failed to decrypt app token")?; + + let token: Dictionary = plist::from_bytes(&decrypted_token) + .context("Failed to parse decrypted app token plist")?; + + let status = token + .get_signed_integer("status-code") + .context("Failed to get status code from app token")?; + if status != 200 { + bail!("App token request failed with status code {}", status); + } + let token_dict = token + .get_dict("t") + .context("Failed to get token dictionary from app token")?; + + Ok(token_dict.clone()) + } + + fn create_session_key(usr: &SrpClientVerifier, name: &str) -> Result, Report> { + Ok( + as hmac::Mac>::new_from_slice(&usr.key())? + .chain_update(name.as_bytes()) + .finalize() + .into_bytes() + .to_vec(), + ) } fn decrypt_cbc(usr: &SrpClientVerifier, data: &[u8]) -> Result, Report> { @@ -581,6 +666,43 @@ impl AppleAccount { .decrypt_padded_vec_mut::(&data)?, ) } + + fn decrypt_gcm(data: &[u8], key: &[u8]) -> Result, Report> { + if data.len() < 3 + 16 + 16 { + bail!( + "Encrypted token is too short to be valid (only {} bytes)", + data.len() + ); + } + let header = &data[0..3]; + if header != b"XYZ" { + bail!( + "Encrypted token is in an unknown format: {}", + String::from_utf8_lossy(header) + ); + } + let iv = &data[3..19]; + let ciphertext_and_tag = &data[19..]; + + if key.len() != 32 { + bail!("Session key is not the correct length: {} bytes", key.len()); + } + if iv.len() != 16 { + bail!("IV is not the correct length: {} bytes", iv.len()); + } + + let key = aes_gcm::Key::>::from_slice(key); + let cipher = AesGcm::::new(key); + let nonce = Nonce::::from_slice(iv); + + let mut buf = ciphertext_and_tag.to_vec(); + + cipher + .decrypt_in_place(nonce, header, &mut buf) + .map_err(|e| report!("Failed to decrypt gcm: {}", e))?; + + Ok(buf) + } } impl std::fmt::Display for AppleAccount { diff --git a/isideload/src/auth/grandslam.rs b/isideload/src/auth/grandslam.rs index ee0f4cf..5a6edf6 100644 --- a/isideload/src/auth/grandslam.rs +++ b/isideload/src/auth/grandslam.rs @@ -8,6 +8,7 @@ use rootcause::prelude::*; use tracing::debug; use crate::{ + SideloadError, anisette::AnisetteClientInfo, util::plist::{PlistDataExtract, plist_to_xml_string}, }; @@ -166,24 +167,18 @@ impl GrandSlam { } pub trait GrandSlamErrorChecker { - fn check_grandslam_error(self) -> Result>; -} - -#[derive(Debug, thiserror::Error)] -pub enum GrandSlamError { - #[error("Auth error {0}: {1}")] - AuthWithMessage(i64, String), + fn check_grandslam_error(self) -> Result>; } impl GrandSlamErrorChecker for Dictionary { - fn check_grandslam_error(self) -> Result> { + fn check_grandslam_error(self) -> Result> { let result = match self.get("Status") { Some(plist::Value::Dictionary(d)) => d, _ => &self, }; if result.get_signed_integer("ec").unwrap_or(0) != 0 { - bail!(GrandSlamError::AuthWithMessage( + bail!(SideloadError::AuthWithMessage( result.get_signed_integer("ec").unwrap_or(-1), result.get_str("em").unwrap_or("Unknown error").to_string(), )) diff --git a/isideload/src/lib.rs b/isideload/src/lib.rs index 3253576..5a03840 100644 --- a/isideload/src/lib.rs +++ b/isideload/src/lib.rs @@ -7,6 +7,15 @@ pub mod anisette; pub mod auth; pub mod util; +#[derive(Debug, thiserror::Error)] +pub enum SideloadError { + #[error("Auth error {0}: {1}")] + AuthWithMessage(i64, String), + + #[error("Plist parse error: {0}")] + PlistParseError(String), +} + struct ReqwestErrorFormatter; impl ContextFormatterHook for ReqwestErrorFormatter { diff --git a/isideload/src/util/plist.rs b/isideload/src/util/plist.rs index b593125..35528cb 100644 --- a/isideload/src/util/plist.rs +++ b/isideload/src/util/plist.rs @@ -1,7 +1,8 @@ +use plist::Dictionary; use plist_macro::{plist_to_xml_bytes, plist_value_to_xml_bytes, pretty_print_dictionary}; use rootcause::prelude::*; -pub fn plist_to_xml_string(p: &plist::Dictionary) -> String { +pub fn plist_to_xml_string(p: &Dictionary) -> String { String::from_utf8(plist_to_xml_bytes(p)).unwrap() } @@ -9,24 +10,59 @@ pub fn plist_value_to_xml_string(p: &plist::Value) -> String { String::from_utf8(plist_value_to_xml_bytes(p)).unwrap() } +pub struct SensitivePlistAttachment { + pub plist: Dictionary, +} + +impl SensitivePlistAttachment { + pub fn new(plist: Dictionary) -> Self { + SensitivePlistAttachment { plist } + } + + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // if env variable DEBUG_SENSITIVE is set, print full plist + if std::env::var("DEBUG_SENSITIVE").is_ok() { + return writeln!(f, "{}", pretty_print_dictionary(&self.plist)); + } + writeln!( + f, + "" + ) + } +} + +impl std::fmt::Display for SensitivePlistAttachment { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.fmt(f) + } +} + +impl std::fmt::Debug for SensitivePlistAttachment { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.fmt(f) + } +} + pub trait PlistDataExtract { fn get_data(&self, key: &str) -> Result<&[u8], Report>; fn get_str(&self, key: &str) -> Result<&str, Report>; fn get_string(&self, key: &str) -> Result; fn get_signed_integer(&self, key: &str) -> Result; - fn get_dict(&self, key: &str) -> Result<&plist::Dictionary, Report>; + fn get_dict(&self, key: &str) -> Result<&Dictionary, Report>; } -impl PlistDataExtract for plist::Dictionary { +impl PlistDataExtract for Dictionary { fn get_data(&self, key: &str) -> Result<&[u8], Report> { self.get(key).and_then(|v| v.as_data()).ok_or_else(|| { - report!("Plist missing data for key '{}'", key).attach(pretty_print_dictionary(self)) + report!("Plist missing data for key '{}'", key) + .attach(SensitivePlistAttachment::new(self.clone())) }) } fn get_str(&self, key: &str) -> Result<&str, Report> { self.get(key).and_then(|v| v.as_string()).ok_or_else(|| { - report!("Plist missing string for key '{}'", key).attach(pretty_print_dictionary(self)) + report!("Plist missing string for key '{}'", key) + .attach(SensitivePlistAttachment::new(self.clone())) }) } @@ -36,7 +72,7 @@ impl PlistDataExtract for plist::Dictionary { .map(|s| s.to_string()) .ok_or_else(|| { report!("Plist missing string for key '{}'", key) - .attach(pretty_print_dictionary(self)) + .attach(SensitivePlistAttachment::new(self.clone())) }) } @@ -45,16 +81,16 @@ impl PlistDataExtract for plist::Dictionary { .and_then(|v| v.as_signed_integer()) .ok_or_else(|| { report!("Plist missing signed integer for key '{}'", key) - .attach(pretty_print_dictionary(self)) + .attach(SensitivePlistAttachment::new(self.clone())) }) } - fn get_dict(&self, key: &str) -> Result<&plist::Dictionary, Report> { + fn get_dict(&self, key: &str) -> Result<&Dictionary, Report> { self.get(key) .and_then(|v| v.as_dictionary()) .ok_or_else(|| { report!("Plist missing dictionary for key '{}'", key) - .attach(pretty_print_dictionary(self)) + .attach(SensitivePlistAttachment::new(self.clone())) }) } } From d6f15c21028d0b9d0e0a14f7275d8a1be60f6b1c Mon Sep 17 00:00:00 2001 From: nab138 Date: Mon, 26 Jan 2026 22:27:43 -0500 Subject: [PATCH 22/71] remove loggin app token --- examples/minimal/src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 7d5c170..c7d9ce1 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -3,7 +3,6 @@ use std::env; use isideload::{ anisette::remote_v3::RemoteV3AnisetteProvider, auth::apple_account::AppleAccountBuilder, }; -use plist_macro::pretty_print_dictionary; use tracing::Level; use tracing_subscriber::FmtSubscriber; @@ -45,7 +44,7 @@ async fn main() { let app_token = account.unwrap().get_app_token("xcode.auth").await; match app_token { - Ok(t) => println!("App token: {}", pretty_print_dictionary(&t)), + Ok(t) => println!("App token acquired"), Err(e) => eprintln!("Failed to get app token: {:?}", e), } } From 0b69a0b2382a4de3914ce9358a0c36e1d3d77276 Mon Sep 17 00:00:00 2001 From: nab138 Date: Tue, 27 Jan 2026 10:39:12 -0500 Subject: [PATCH 23/71] add developer session struct --- isideload/src/dev/developer_session.rs | 13 +++++++++++++ isideload/src/dev/mod.rs | 1 + isideload/src/lib.rs | 1 + 3 files changed, 15 insertions(+) create mode 100644 isideload/src/dev/developer_session.rs create mode 100644 isideload/src/dev/mod.rs diff --git a/isideload/src/dev/developer_session.rs b/isideload/src/dev/developer_session.rs new file mode 100644 index 0000000..4c27197 --- /dev/null +++ b/isideload/src/dev/developer_session.rs @@ -0,0 +1,13 @@ +use std::sync::Arc; + +use crate::auth::apple_account::AppleAccount; + +struct DeveloperSession { + apple_account: Arc, +} + +impl DeveloperSession { + pub fn new(apple_account: Arc) -> Self { + DeveloperSession { apple_account } + } +} diff --git a/isideload/src/dev/mod.rs b/isideload/src/dev/mod.rs new file mode 100644 index 0000000..09da96f --- /dev/null +++ b/isideload/src/dev/mod.rs @@ -0,0 +1 @@ +pub mod developer_session; diff --git a/isideload/src/lib.rs b/isideload/src/lib.rs index 5a03840..9fc2232 100644 --- a/isideload/src/lib.rs +++ b/isideload/src/lib.rs @@ -5,6 +5,7 @@ use rootcause::{ pub mod anisette; pub mod auth; +pub mod dev; pub mod util; #[derive(Debug, thiserror::Error)] From aee5eaf26e8068a850ff2f21c3492d076ed47eb9 Mon Sep 17 00:00:00 2001 From: nab138 Date: Tue, 27 Jan 2026 23:00:30 -0500 Subject: [PATCH 24/71] Developer API implimentation --- Cargo.lock | 88 +----------------- examples/minimal/Cargo.toml | 1 + examples/minimal/src/main.rs | 27 ++++-- isideload/Cargo.toml | 3 +- isideload/src/anisette/mod.rs | 18 ++-- isideload/src/anisette/remote_v3/mod.rs | 5 +- isideload/src/auth/apple_account.rs | 115 +++++++----------------- isideload/src/auth/builder.rs | 73 +++++++++++++++ isideload/src/auth/grandslam.rs | 18 ++-- isideload/src/auth/mod.rs | 1 + isideload/src/dev/developer_session.rs | 99 ++++++++++++++++++-- isideload/src/dev/device_type.rs | 26 ++++++ isideload/src/dev/mod.rs | 1 + 13 files changed, 271 insertions(+), 204 deletions(-) create mode 100644 isideload/src/auth/builder.rs create mode 100644 isideload/src/dev/device_type.rs diff --git a/Cargo.lock b/Cargo.lock index bc9f3a5..b700466 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,15 +43,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "async-compression" version = "0.4.37" @@ -190,19 +181,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" -[[package]] -name = "chrono" -version = "0.4.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "wasm-bindgen", - "windows-link", -] - [[package]] name = "cipher" version = "0.4.4" @@ -661,30 +639,6 @@ dependencies = [ "windows-registry", ] -[[package]] -name = "iana-time-zone" -version = "0.1.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - [[package]] name = "icu_collections" version = "2.1.1" @@ -849,7 +803,6 @@ dependencies = [ "async-trait", "base64", "cbc", - "chrono", "futures-util", "hex", "hmac", @@ -965,6 +918,7 @@ name = "minimal" version = "0.1.0" dependencies = [ "isideload", + "plist", "plist-macro", "tokio", "tracing", @@ -2018,10 +1972,11 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" dependencies = [ + "getrandom 0.3.4", "js-sys", "wasm-bindgen", ] @@ -2187,41 +2142,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-link" version = "0.2.1" diff --git a/examples/minimal/Cargo.toml b/examples/minimal/Cargo.toml index cdafa1f..8b737d3 100644 --- a/examples/minimal/Cargo.toml +++ b/examples/minimal/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] isideload = { path = "../../isideload" } +plist = "1.8.0" plist-macro = "0.1.3" tokio = { version = "1.49.0", features = ["rt-multi-thread", "macros"] } tracing = "0.1.44" diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index c7d9ce1..aec33c1 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -1,16 +1,19 @@ use std::env; use isideload::{ - anisette::remote_v3::RemoteV3AnisetteProvider, auth::apple_account::AppleAccountBuilder, + anisette::remote_v3::RemoteV3AnisetteProvider, auth::apple_account::AppleAccount, + dev::developer_session::DeveloperSession, }; -use tracing::Level; + +use plist_macro::pretty_print_dictionary; +use tracing::{Level, debug}; use tracing_subscriber::FmtSubscriber; #[tokio::main] async fn main() { isideload::init().expect("Failed to initialize error reporting"); let subscriber = FmtSubscriber::builder() - .with_max_level(Level::DEBUG) + .with_max_level(Level::INFO) .finish(); tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); @@ -31,7 +34,7 @@ async fn main() { Some(code.trim().to_string()) }; - let account = AppleAccountBuilder::new(apple_id) + let account = AppleAccount::builder(apple_id) .anisette_provider(RemoteV3AnisetteProvider::default().set_serial_number("2".to_string())) .login(apple_password, get_2fa_code) .await; @@ -41,10 +44,16 @@ async fn main() { Err(e) => eprintln!("Failed to log in to Apple ID: {:?}", e), } - let app_token = account.unwrap().get_app_token("xcode.auth").await; + let mut account = account.unwrap(); - match app_token { - Ok(t) => println!("App token acquired"), - Err(e) => eprintln!("Failed to get app token: {:?}", e), - } + let dev_session = DeveloperSession::from_account(&mut account) + .await + .expect("Failed to create developer session"); + + let res = dev_session + .list_teams() + .await + .expect("Failed to list teams"); + + println!("{}", pretty_print_dictionary(&res)); } diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index b200d13..657d926 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -20,11 +20,10 @@ plist = "1.8" plist-macro = "0.1.3" reqwest = { version = "0.13.1", features = ["json", "gzip"] } thiserror = "2.0.17" -chrono = "0.4.43" async-trait = "0.1.89" serde = "1.0.228" rand = "0.9.2" -uuid = "1.19.0" +uuid = {version = "1.20.0", features = ["v4"] } sha2 = "0.10.9" tracing = "0.1.44" tokio-tungstenite = { version = "0.28.0", features = ["rustls-tls-webpki-roots"] } diff --git a/isideload/src/anisette/mod.rs b/isideload/src/anisette/mod.rs index 1894915..4514634 100644 --- a/isideload/src/anisette/mod.rs +++ b/isideload/src/anisette/mod.rs @@ -1,7 +1,6 @@ pub mod remote_v3; use crate::auth::grandslam::GrandSlam; -use chrono::{DateTime, SubsecRound, Utc}; use plist::Dictionary; use plist_macro::plist; use reqwest::header::HeaderMap; @@ -20,14 +19,15 @@ pub struct AnisetteData { machine_id: String, one_time_password: String, pub routing_info: String, - device_description: String, + _device_description: String, device_unique_identifier: String, - local_user_id: String, + _local_user_id: String, } +// Some headers don't seem to be required. I guess not including them is technically more efficient soooo impl AnisetteData { - pub fn get_headers(&self, serial: String) -> HashMap { - // let dt: DateTime = Utc::now().round_subsecs(0); + pub fn get_headers(&self) -> HashMap { + //let dt: DateTime = Utc::now().round_subsecs(0); HashMap::from_iter( [ @@ -55,8 +55,8 @@ impl AnisetteData { ) } - pub fn get_header_map(&self, serial: String) -> HeaderMap { - let headers_map = self.get_headers(serial); + pub fn get_header_map(&self) -> HeaderMap { + let headers_map = self.get_headers(); let mut header_map = HeaderMap::new(); for (key, value) in headers_map { @@ -69,8 +69,8 @@ impl AnisetteData { header_map } - pub fn get_client_provided_data(&self, serial: String) -> Dictionary { - let headers = self.get_headers(serial); + pub fn get_client_provided_data(&self) -> Dictionary { + let headers = self.get_headers(); let mut cpd = plist!(dict { "bootstrap": "true", diff --git a/isideload/src/anisette/remote_v3/mod.rs b/isideload/src/anisette/remote_v3/mod.rs index d292734..8a39140 100644 --- a/isideload/src/anisette/remote_v3/mod.rs +++ b/isideload/src/anisette/remote_v3/mod.rs @@ -4,7 +4,6 @@ use std::fs; use std::path::PathBuf; use base64::prelude::*; -// use chrono::{SubsecRound, Utc}; use plist_macro::plist; use reqwest::header::{CONTENT_TYPE, HeaderMap, HeaderValue}; use rootcause::prelude::*; @@ -110,9 +109,9 @@ impl AnisetteProvider for RemoteV3AnisetteProvider { machine_id, one_time_password, routing_info, - device_description: client_info, + _device_description: client_info, device_unique_identifier: state.get_device_id(), - local_user_id: hex::encode(&state.get_md_lu()), + _local_user_id: hex::encode(&state.get_md_lu()), }; Ok(data) diff --git a/isideload/src/auth/apple_account.rs b/isideload/src/auth/apple_account.rs index 09b6def..4f60d9f 100644 --- a/isideload/src/auth/apple_account.rs +++ b/isideload/src/auth/apple_account.rs @@ -1,6 +1,9 @@ use crate::{ - anisette::{AnisetteData, AnisetteProvider, remote_v3::RemoteV3AnisetteProvider}, - auth::grandslam::{GrandSlam, GrandSlamErrorChecker}, + anisette::{AnisetteData, AnisetteProvider}, + auth::{ + builder::AppleAccountBuilder, + grandslam::{GrandSlam, GrandSlamErrorChecker}, + }, util::plist::PlistDataExtract, }; use aes::{ @@ -22,8 +25,6 @@ use srp::{ }; use tracing::{debug, info, warn}; -const SERIAL_NUMBER: &str = "2"; - pub struct AppleAccount { pub email: String, pub spd: Option, @@ -34,12 +35,6 @@ pub struct AppleAccount { debug: bool, } -pub struct AppleAccountBuilder { - email: String, - debug: Option, - anisette_provider: Option>, -} - #[derive(Debug)] pub enum LoginState { LoggedIn, @@ -49,67 +44,6 @@ pub enum LoginState { NeedsLogin, } -impl AppleAccountBuilder { - /// Create a new AppleAccountBuilder with the given email - /// - /// # Arguments - /// - `email`: The Apple ID email address - pub fn new(email: &str) -> Self { - Self { - email: email.to_string(), - debug: None, - anisette_provider: None, - } - } - - /// DANGER Set whether to enable debug mode - /// - /// # Arguments - /// - `debug`: If true, accept invalid certificates and enable verbose connection logging - pub fn danger_debug(mut self, debug: bool) -> Self { - self.debug = Some(debug); - self - } - - pub fn anisette_provider(mut self, anisette_provider: impl AnisetteProvider + 'static) -> Self { - self.anisette_provider = Some(Box::new(anisette_provider)); - self - } - - /// Build the AppleAccount without logging in - /// - /// # Errors - /// Returns an error if the reqwest client cannot be built - pub async fn build(self) -> Result { - let debug = self.debug.unwrap_or(false); - let anisette_provider = self - .anisette_provider - .unwrap_or_else(|| Box::new(RemoteV3AnisetteProvider::default())); - - AppleAccount::new(&self.email, anisette_provider, debug).await - } - - /// Build the AppleAccount and log in - /// - /// # Arguments - /// - `password`: The Apple ID password - /// - `two_factor_callback`: A callback function that returns the two-factor authentication code - /// # Errors - /// Returns an error if the reqwest client cannot be built - pub async fn login( - self, - password: &str, - two_factor_callback: F, - ) -> Result - where - F: Fn() -> Option, - { - let mut account = self.build().await?; - account.login(password, two_factor_callback).await?; - Ok(account) - } -} - impl AppleAccount { /// Create a new AppleAccountBuilder with the given email /// @@ -395,7 +329,7 @@ impl AppleAccount { } async fn build_2fa_headers(&mut self) -> Result { - let mut headers = self.anisette_data.get_header_map(SERIAL_NUMBER.to_string()); + let mut headers = self.anisette_data.get_header_map(); let spd = self .spd @@ -427,9 +361,7 @@ impl AppleAccount { debug!("GrandSlam service URL: {}", gs_service_url); - let cpd = self - .anisette_data - .get_client_provided_data(SERIAL_NUMBER.to_string()); + let cpd = self.anisette_data.get_client_provided_data(); let srp_client = SrpClient::::new(&G_2048); let a: Vec = (0..32).map(|_| rand::random::()).collect(); @@ -567,7 +499,7 @@ impl AppleAccount { Ok(LoginState::LoggedIn) } - pub async fn get_app_token(&mut self, app: &str) -> Result { + pub async fn get_app_token(&mut self, app: &str) -> Result { let app = if app.contains("com.apple.gs.") { app.to_string() } else { @@ -596,16 +528,14 @@ impl AppleAccount { .to_vec(); let gs_service_url = self.grandslam_client.get_url("gsService").await?; - let cpd = self - .anisette_data - .get_client_provided_data(SERIAL_NUMBER.to_string()); + let cpd = self.anisette_data.get_client_provided_data(); let request = plist!(dict { "Header": { "Version": "1.0.1" }, "Request": { - "app": [app], + "app": [app.clone()], "c": c, "checksum": checksum, "cpd": cpd, @@ -642,8 +572,24 @@ impl AppleAccount { let token_dict = token .get_dict("t") .context("Failed to get token dictionary from app token")?; + let app_token = token_dict + .get_dict(&app) + .context("Failed to get app token string")?; - Ok(token_dict.clone()) + let app_token = AppToken { + token: app_token + .get_str("token") + .context("Failed to get app token string")? + .to_string(), + duration: app_token + .get_signed_integer("duration") + .context("Failed to get app token duration")? as u64, + expiry: app_token + .get_signed_integer("expiry") + .context("Failed to get app token expiry")? as u64, + }; + + Ok(app_token) } fn create_session_key(usr: &SrpClientVerifier, name: &str) -> Result, Report> { @@ -715,3 +661,10 @@ impl std::fmt::Display for AppleAccount { write!(f, "{} ({:?})", self.email, self.login_state) } } + +#[derive(Debug)] +pub struct AppToken { + pub token: String, + pub duration: u64, + pub expiry: u64, +} diff --git a/isideload/src/auth/builder.rs b/isideload/src/auth/builder.rs new file mode 100644 index 0000000..18bd03e --- /dev/null +++ b/isideload/src/auth/builder.rs @@ -0,0 +1,73 @@ +use rootcause::prelude::*; + +use crate::{ + anisette::{AnisetteProvider, remote_v3::RemoteV3AnisetteProvider}, + auth::apple_account::AppleAccount, +}; + +pub struct AppleAccountBuilder { + email: String, + debug: Option, + anisette_provider: Option>, +} + +impl AppleAccountBuilder { + /// Create a new AppleAccountBuilder with the given email + /// + /// # Arguments + /// - `email`: The Apple ID email address + pub fn new(email: &str) -> Self { + Self { + email: email.to_string(), + debug: None, + anisette_provider: None, + } + } + + /// DANGER Set whether to enable debug mode + /// + /// # Arguments + /// - `debug`: If true, accept invalid certificates and enable verbose connection logging + pub fn danger_debug(mut self, debug: bool) -> Self { + self.debug = Some(debug); + self + } + + pub fn anisette_provider(mut self, anisette_provider: impl AnisetteProvider + 'static) -> Self { + self.anisette_provider = Some(Box::new(anisette_provider)); + self + } + + /// Build the AppleAccount without logging in + /// + /// # Errors + /// Returns an error if the reqwest client cannot be built + pub async fn build(self) -> Result { + let debug = self.debug.unwrap_or(false); + let anisette_provider = self + .anisette_provider + .unwrap_or_else(|| Box::new(RemoteV3AnisetteProvider::default())); + + AppleAccount::new(&self.email, anisette_provider, debug).await + } + + /// Build the AppleAccount and log in + /// + /// # Arguments + /// - `password`: The Apple ID password + /// - `two_factor_callback`: A callback function that returns the two-factor authentication code + /// # Errors + /// Returns an error if the reqwest client cannot be built + pub async fn login( + self, + password: &str, + two_factor_callback: F, + ) -> Result + where + F: Fn() -> Option, + { + let mut account = self.build().await?; + account.login(password, two_factor_callback).await?; + Ok(account) + } +} diff --git a/isideload/src/auth/grandslam.rs b/isideload/src/auth/grandslam.rs index 5a6edf6..71d1800 100644 --- a/isideload/src/auth/grandslam.rs +++ b/isideload/src/auth/grandslam.rs @@ -134,15 +134,15 @@ impl GrandSlam { "X-Mme-Client-Info", HeaderValue::from_str(&self.client_info.client_info)?, ); - // headers.insert( - // "User-Agent", - // HeaderValue::from_str(&self.client_info.user_agent)?, - // ); - // headers.insert("X-Xcode-Version", HeaderValue::from_static("14.2 (14C18)")); - // headers.insert( - // "X-Apple-App-Info", - // HeaderValue::from_static("com.apple.gs.xcode.auth"), - // ); + headers.insert( + "User-Agent", + HeaderValue::from_str(&self.client_info.user_agent)?, + ); + headers.insert("X-Xcode-Version", HeaderValue::from_static("14.2 (14C18)")); + headers.insert( + "X-Apple-App-Info", + HeaderValue::from_static("com.apple.gs.xcode.auth"), + ); Ok(headers) } diff --git a/isideload/src/auth/mod.rs b/isideload/src/auth/mod.rs index 9aa7ce1..8ef46dc 100644 --- a/isideload/src/auth/mod.rs +++ b/isideload/src/auth/mod.rs @@ -1,2 +1,3 @@ pub mod apple_account; +pub mod builder; pub mod grandslam; diff --git a/isideload/src/dev/developer_session.rs b/isideload/src/dev/developer_session.rs index 4c27197..18e351b 100644 --- a/isideload/src/dev/developer_session.rs +++ b/isideload/src/dev/developer_session.rs @@ -1,13 +1,98 @@ -use std::sync::Arc; +use plist::Dictionary; +use plist_macro::plist; +use rootcause::prelude::*; +use uuid::Uuid; -use crate::auth::apple_account::AppleAccount; +use crate::{ + anisette::AnisetteData, + auth::{ + apple_account::{AppToken, AppleAccount}, + grandslam::GrandSlam, + }, + dev::device_type::DeveloperDeviceType, + util::plist::{PlistDataExtract, plist_to_xml_string}, +}; -struct DeveloperSession { - apple_account: Arc, +pub struct DeveloperSession<'a> { + token: AppToken, + adsid: String, + client: &'a GrandSlam, + anisette_data: &'a AnisetteData, } -impl DeveloperSession { - pub fn new(apple_account: Arc) -> Self { - DeveloperSession { apple_account } +impl<'a> DeveloperSession<'a> { + pub fn new( + token: AppToken, + adsid: String, + client: &'a GrandSlam, + anisette_data: &'a AnisetteData, + ) -> Self { + DeveloperSession { + token, + adsid, + client, + anisette_data, + } + } + + pub async fn from_account(account: &'a mut AppleAccount) -> Result { + let token = account + .get_app_token("xcode.auth") + .await + .context("Failed to get xcode token from Apple account")?; + + let spd = account + .spd + .as_ref() + .ok_or_else(|| report!("SPD not available, cannot get adsid"))?; + + Ok(DeveloperSession::new( + token, + spd.get_string("adsid")?, + &account.grandslam_client, + &account.anisette_data, + )) + } + + pub async fn send_developer_request( + &self, + url: &str, + body: Option, + ) -> Result { + let body = body.unwrap_or_else(|| Dictionary::new()); + + let base = plist!(dict { + "clientId": "XABBG36SBA", + "protocolVersion": "QH65B2", + "requestId": Uuid::new_v4().to_string().to_uppercase(), + "userLocale": ["en_US"], + }); + + let body = base.into_iter().chain(body.into_iter()).collect(); + + let text = self + .client + .post(url)? + .body(plist_to_xml_string(&body)) + .header("X-Apple-GS-Token", &self.token.token) + .header("X-Apple-I-Identity-Id", &self.adsid) + .headers(self.anisette_data.get_header_map()) + .send() + .await? + .error_for_status() + .context("Developer request failed")? + .text() + .await + .context("Failed to read developer request response text")?; + + let dict: Dictionary = plist::from_bytes(text.as_bytes()) + .context("Failed to parse developer request plist")?; + + Ok(dict) + } + + pub async fn list_teams(&self) -> Result { + self.send_developer_request(&DeveloperDeviceType::Any.dev_url("listTeams"), None) + .await } } diff --git a/isideload/src/dev/device_type.rs b/isideload/src/dev/device_type.rs new file mode 100644 index 0000000..bcc5fb5 --- /dev/null +++ b/isideload/src/dev/device_type.rs @@ -0,0 +1,26 @@ +#[derive(Debug, Clone)] +pub enum DeveloperDeviceType { + Any, + Ios, + Tvos, + Watchos, +} + +impl DeveloperDeviceType { + pub fn url_segment(&self) -> &'static str { + match self { + DeveloperDeviceType::Any => "", + DeveloperDeviceType::Ios => "ios/", + DeveloperDeviceType::Tvos => "tvos/", + DeveloperDeviceType::Watchos => "watchos/", + } + } + + pub fn dev_url(&self, endpoint: &str) -> String { + format!( + "https://developerservices2.apple.com/services/QH65B2/{}{}.action?clientId=XABBG36SBA", + self.url_segment(), + endpoint, + ) + } +} diff --git a/isideload/src/dev/mod.rs b/isideload/src/dev/mod.rs index 09da96f..dd3c3fb 100644 --- a/isideload/src/dev/mod.rs +++ b/isideload/src/dev/mod.rs @@ -1 +1,2 @@ pub mod developer_session; +pub mod device_type; From 82881aa4e10b156c20bf5ec59d7fe456be8d7846 Mon Sep 17 00:00:00 2001 From: nab138 Date: Wed, 28 Jan 2026 14:45:03 -0500 Subject: [PATCH 25/71] Properly impliment list teams and fix cert on windows --- Cargo.lock | 28 +++++++++++++++----------- Cargo.toml | 3 +++ examples/minimal/src/main.rs | 2 +- isideload/Cargo.toml | 2 +- isideload/src/dev/developer_session.rs | 28 ++++++++++++++++++-------- isideload/src/dev/mod.rs | 1 + isideload/src/dev/structures.rs | 17 ++++++++++++++++ 7 files changed, 59 insertions(+), 22 deletions(-) create mode 100644 isideload/src/dev/structures.rs diff --git a/Cargo.lock b/Cargo.lock index b700466..1bf6410 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,6 +106,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.10.0" @@ -1355,8 +1361,7 @@ dependencies = [ [[package]] name = "rustls-platform-verifier" version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +source = "git+https://github.com/cstkingkey/rustls-platform-verifier?branch=extra#1efde30e9080351dc1afd74151ea5b849f1e69f9" dependencies = [ "core-foundation 0.10.1", "core-foundation-sys", @@ -1370,14 +1375,13 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] name = "rustls-platform-verifier-android" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" +source = "git+https://github.com/cstkingkey/rustls-platform-verifier?branch=extra#1efde30e9080351dc1afd74151ea5b849f1e69f9" [[package]] name = "rustls-webpki" @@ -1421,7 +1425,7 @@ version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ - "bitflags", + "bitflags 2.10.0", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -1591,20 +1595,20 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.6.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation 0.9.4", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.6.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" dependencies = [ "core-foundation-sys", "libc", @@ -1802,7 +1806,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "async-compression", - "bitflags", + "bitflags 2.10.0", "bytes", "futures-core", "futures-util", diff --git a/Cargo.toml b/Cargo.toml index b546fb1..9dc06e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,3 +2,6 @@ resolver = "2" members = ["examples/minimal","isideload"] default-members = ["isideload"] + +[patch.crates-io] +rustls-platform-verifier = { git = "https://github.com/cstkingkey/rustls-platform-verifier", branch = "extra" } \ No newline at end of file diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index aec33c1..9a3a305 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -55,5 +55,5 @@ async fn main() { .await .expect("Failed to list teams"); - println!("{}", pretty_print_dictionary(&res)); + println!("{:#?}", res); } diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 657d926..7ed3adb 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -37,4 +37,4 @@ pbkdf2 = "0.12.2" hmac = "0.12.1" cbc = { version = "0.1.2", features = ["std"] } aes = "0.8.4" -aes-gcm = "0.10.3" +aes-gcm = "0.10.3" \ No newline at end of file diff --git a/isideload/src/dev/developer_session.rs b/isideload/src/dev/developer_session.rs index 18e351b..7966817 100644 --- a/isideload/src/dev/developer_session.rs +++ b/isideload/src/dev/developer_session.rs @@ -1,6 +1,7 @@ use plist::Dictionary; use plist_macro::plist; use rootcause::prelude::*; +use tracing::debug; use uuid::Uuid; use crate::{ @@ -9,7 +10,10 @@ use crate::{ apple_account::{AppToken, AppleAccount}, grandslam::GrandSlam, }, - dev::device_type::DeveloperDeviceType, + dev::{ + device_type::DeveloperDeviceType, + structures::{DeveloperTeam, ListTeamResponse}, + }, util::plist::{PlistDataExtract, plist_to_xml_string}, }; @@ -58,7 +62,7 @@ impl<'a> DeveloperSession<'a> { &self, url: &str, body: Option, - ) -> Result { + ) -> Result { let body = body.unwrap_or_else(|| Dictionary::new()); let base = plist!(dict { @@ -85,14 +89,22 @@ impl<'a> DeveloperSession<'a> { .await .context("Failed to read developer request response text")?; - let dict: Dictionary = plist::from_bytes(text.as_bytes()) - .context("Failed to parse developer request plist")?; + // let dict: Dictionary = plist::from_bytes(text.as_bytes()) + // .context("Failed to parse developer request plist")?; - Ok(dict) + Ok(text) } - pub async fn list_teams(&self) -> Result { - self.send_developer_request(&DeveloperDeviceType::Any.dev_url("listTeams"), None) - .await + pub async fn list_teams(&self) -> Result, Report> { + let res = self + .send_developer_request(&DeveloperDeviceType::Any.dev_url("listTeams"), None) + .await?; + + let response: ListTeamResponse = plist::from_bytes(res.as_bytes()) + .context("Failed to parse list teams response plist")?; + + debug!("List Teams Response Code: {:?}", response.result_code); + + Ok(response.teams) } } diff --git a/isideload/src/dev/mod.rs b/isideload/src/dev/mod.rs index dd3c3fb..11420e3 100644 --- a/isideload/src/dev/mod.rs +++ b/isideload/src/dev/mod.rs @@ -1,2 +1,3 @@ pub mod developer_session; pub mod device_type; +pub mod structures; \ No newline at end of file diff --git a/isideload/src/dev/structures.rs b/isideload/src/dev/structures.rs new file mode 100644 index 0000000..1516cd5 --- /dev/null +++ b/isideload/src/dev/structures.rs @@ -0,0 +1,17 @@ +use serde::Deserialize; + +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct DeveloperTeam { + name: String, + team_id: String, + r#type: String, + status: String, +} + +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct ListTeamResponse { + pub teams: Vec, + pub result_code: i64, +} From f01fcc0ac04d475440336c90a9b1b50cd4424c5b Mon Sep 17 00:00:00 2001 From: nab138 Date: Wed, 28 Jan 2026 22:33:51 -0500 Subject: [PATCH 26/71] Add list devices and refactor --- Cargo.lock | 3 +- Cargo.toml | 3 +- examples/minimal/src/main.rs | 11 +++- isideload/src/auth/grandslam.rs | 7 +-- isideload/src/dev/developer_session.rs | 80 +++++++++++++++++++------- isideload/src/dev/device_type.rs | 26 --------- isideload/src/dev/mod.rs | 3 +- isideload/src/dev/structures.rs | 45 +++++++++++++-- isideload/src/util/plist.rs | 10 +--- 9 files changed, 117 insertions(+), 71 deletions(-) delete mode 100644 isideload/src/dev/device_type.rs diff --git a/Cargo.lock b/Cargo.lock index 1bf6410..af26310 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1071,8 +1071,7 @@ dependencies = [ [[package]] name = "plist-macro" version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8888e02e251eba3258cc58fb79f0d8675c34b3428749e738562d58a0271bf035" +source = "git+https://github.com/nab138/plist_macro?branch=master#7004b1625aa50044bd381ee5393a9c85cec039d5" dependencies = [ "plist", ] diff --git a/Cargo.toml b/Cargo.toml index 9dc06e1..5fb16d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,5 @@ members = ["examples/minimal","isideload"] default-members = ["isideload"] [patch.crates-io] -rustls-platform-verifier = { git = "https://github.com/cstkingkey/rustls-platform-verifier", branch = "extra" } \ No newline at end of file +rustls-platform-verifier = { git = "https://github.com/cstkingkey/rustls-platform-verifier", branch = "extra" } +plist-macro = { git = "https://github.com/nab138/plist_macro", branch = "master" } \ No newline at end of file diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 9a3a305..af4497f 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -50,10 +50,19 @@ async fn main() { .await .expect("Failed to create developer session"); - let res = dev_session + let teams = dev_session .list_teams() .await .expect("Failed to list teams"); + let team = teams + .get(0) + .expect("No developer teams available for this account"); + + let res = dev_session + .list_devices(team, None) + .await + .expect("Failed to list developer devices"); + println!("{:#?}", res); } diff --git a/isideload/src/auth/grandslam.rs b/isideload/src/auth/grandslam.rs index 71d1800..44133fa 100644 --- a/isideload/src/auth/grandslam.rs +++ b/isideload/src/auth/grandslam.rs @@ -1,4 +1,5 @@ use plist::Dictionary; +use plist_macro::plist_to_xml_string; use plist_macro::pretty_print_dictionary; use reqwest::{ Certificate, ClientBuilder, @@ -7,11 +8,7 @@ use reqwest::{ use rootcause::prelude::*; use tracing::debug; -use crate::{ - SideloadError, - anisette::AnisetteClientInfo, - util::plist::{PlistDataExtract, plist_to_xml_string}, -}; +use crate::{SideloadError, anisette::AnisetteClientInfo, util::plist::PlistDataExtract}; const APPLE_ROOT: &[u8] = include_bytes!("./apple_root.der"); const URL_BAG: &str = "https://gsa.apple.com/grandslam/GsService2/lookup"; diff --git a/isideload/src/dev/developer_session.rs b/isideload/src/dev/developer_session.rs index 7966817..ff1eb2a 100644 --- a/isideload/src/dev/developer_session.rs +++ b/isideload/src/dev/developer_session.rs @@ -1,7 +1,8 @@ use plist::Dictionary; -use plist_macro::plist; +use plist_macro::{plist, plist_to_xml_string}; use rootcause::prelude::*; -use tracing::debug; +use serde::de::DeserializeOwned; +use tracing::warn; use uuid::Uuid; use crate::{ @@ -10,11 +11,12 @@ use crate::{ apple_account::{AppToken, AppleAccount}, grandslam::GrandSlam, }, - dev::{ - device_type::DeveloperDeviceType, - structures::{DeveloperTeam, ListTeamResponse}, + dev::structures::{ + DeveloperDevice, + DeveloperDeviceType::{self, *}, + DeveloperTeam, ListDevicesResponse, ListTeamsResponse, }, - util::plist::{PlistDataExtract, plist_to_xml_string}, + util::plist::PlistDataExtract, }; pub struct DeveloperSession<'a> { @@ -58,12 +60,12 @@ impl<'a> DeveloperSession<'a> { )) } - pub async fn send_developer_request( + pub async fn send_developer_request( &self, url: &str, - body: Option, - ) -> Result { - let body = body.unwrap_or_else(|| Dictionary::new()); + body: impl Into>, + ) -> Result { + let body = body.into().unwrap_or_else(|| Dictionary::new()); let base = plist!(dict { "clientId": "XABBG36SBA", @@ -89,22 +91,60 @@ impl<'a> DeveloperSession<'a> { .await .context("Failed to read developer request response text")?; - // let dict: Dictionary = plist::from_bytes(text.as_bytes()) - // .context("Failed to parse developer request plist")?; + let dict: T = plist::from_bytes(text.as_bytes()) + .context("Failed to parse developer request plist")?; - Ok(text) + Ok(dict) } pub async fn list_teams(&self) -> Result, Report> { - let res = self - .send_developer_request(&DeveloperDeviceType::Any.dev_url("listTeams"), None) - .await?; + let response: ListTeamsResponse = self + .send_developer_request(&dev_url("listTeams", Any), None) + .await + .context("Failed to list developer teams")?; - let response: ListTeamResponse = plist::from_bytes(res.as_bytes()) - .context("Failed to parse list teams response plist")?; - - debug!("List Teams Response Code: {:?}", response.result_code); + if response.result_code != 0 { + warn!( + "Non-zero list teams response code: {}", + response.result_code + ) + }; Ok(response.teams) } + + pub async fn list_devices( + &self, + team: &DeveloperTeam, + device_type: impl Into>, + ) -> Result, Report> { + let body = plist!(dict { + "teamId": &team.team_id, + }); + + let response: ListDevicesResponse = self + .send_developer_request(&dev_url("listDevices", device_type), body) + .await + .context("Failed to list developer devices")?; + + if response.result_code != 0 { + warn!( + "Non-zero list devices response code: {}", + response.result_code + ) + }; + + Ok(response.devices) + } +} + +fn dev_url(endpoint: &str, device_type: impl Into>) -> String { + format!( + "https://developerservices2.apple.com/services/QH65B2/{}{}.action?clientId=XABBG36SBA", + device_type + .into() + .unwrap_or(DeveloperDeviceType::Ios) + .url_segment(), + endpoint, + ) } diff --git a/isideload/src/dev/device_type.rs b/isideload/src/dev/device_type.rs deleted file mode 100644 index bcc5fb5..0000000 --- a/isideload/src/dev/device_type.rs +++ /dev/null @@ -1,26 +0,0 @@ -#[derive(Debug, Clone)] -pub enum DeveloperDeviceType { - Any, - Ios, - Tvos, - Watchos, -} - -impl DeveloperDeviceType { - pub fn url_segment(&self) -> &'static str { - match self { - DeveloperDeviceType::Any => "", - DeveloperDeviceType::Ios => "ios/", - DeveloperDeviceType::Tvos => "tvos/", - DeveloperDeviceType::Watchos => "watchos/", - } - } - - pub fn dev_url(&self, endpoint: &str) -> String { - format!( - "https://developerservices2.apple.com/services/QH65B2/{}{}.action?clientId=XABBG36SBA", - self.url_segment(), - endpoint, - ) - } -} diff --git a/isideload/src/dev/mod.rs b/isideload/src/dev/mod.rs index 11420e3..1a66810 100644 --- a/isideload/src/dev/mod.rs +++ b/isideload/src/dev/mod.rs @@ -1,3 +1,2 @@ pub mod developer_session; -pub mod device_type; -pub mod structures; \ No newline at end of file +pub mod structures; diff --git a/isideload/src/dev/structures.rs b/isideload/src/dev/structures.rs index 1516cd5..39e8851 100644 --- a/isideload/src/dev/structures.rs +++ b/isideload/src/dev/structures.rs @@ -1,17 +1,52 @@ use serde::Deserialize; +#[derive(Debug, Clone)] +pub enum DeveloperDeviceType { + Any, + Ios, + Tvos, + Watchos, +} + +impl DeveloperDeviceType { + pub fn url_segment(&self) -> &'static str { + match self { + DeveloperDeviceType::Any => "", + DeveloperDeviceType::Ios => "ios/", + DeveloperDeviceType::Tvos => "tvos/", + DeveloperDeviceType::Watchos => "watchos/", + } + } +} + #[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct DeveloperTeam { - name: String, - team_id: String, - r#type: String, - status: String, + pub name: Option, + pub team_id: String, + pub r#type: Option, + pub status: Option, } #[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] -pub struct ListTeamResponse { +pub struct ListTeamsResponse { pub teams: Vec, pub result_code: i64, } + +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct DeveloperDevice { + pub name: String, + pub device_id: String, + pub device_number: String, + pub status: Option, +} + +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct ListDevicesResponse { + pub devices: Vec, + pub result_code: i64, +} diff --git a/isideload/src/util/plist.rs b/isideload/src/util/plist.rs index 35528cb..c02e0bc 100644 --- a/isideload/src/util/plist.rs +++ b/isideload/src/util/plist.rs @@ -1,15 +1,7 @@ use plist::Dictionary; -use plist_macro::{plist_to_xml_bytes, plist_value_to_xml_bytes, pretty_print_dictionary}; +use plist_macro::pretty_print_dictionary; use rootcause::prelude::*; -pub fn plist_to_xml_string(p: &Dictionary) -> String { - String::from_utf8(plist_to_xml_bytes(p)).unwrap() -} - -pub fn plist_value_to_xml_string(p: &plist::Value) -> String { - String::from_utf8(plist_value_to_xml_bytes(p)).unwrap() -} - pub struct SensitivePlistAttachment { pub plist: Dictionary, } From c639ed2f2fc6ab37deb80ee97c1752cdd5bfa0fb Mon Sep 17 00:00:00 2001 From: nab138 Date: Thu, 29 Jan 2026 11:12:09 -0500 Subject: [PATCH 27/71] Add list development certs to dev session api --- Cargo.lock | 13 ++++++++++++- examples/minimal/src/main.rs | 5 ++--- isideload/Cargo.toml | 3 ++- isideload/src/dev/developer_session.rs | 27 +++++++++++++++++++++++++- isideload/src/dev/structures.rs | 18 +++++++++++++++++ 5 files changed, 60 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index af26310..2926664 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -821,6 +821,7 @@ dependencies = [ "reqwest", "rootcause", "serde", + "serde_bytes", "serde_json", "sha2", "thiserror 2.0.18", @@ -1374,7 +1375,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -1451,6 +1452,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + [[package]] name = "serde_core" version = "1.0.228" diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index af4497f..ae250d0 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -5,8 +5,7 @@ use isideload::{ dev::developer_session::DeveloperSession, }; -use plist_macro::pretty_print_dictionary; -use tracing::{Level, debug}; +use tracing::Level; use tracing_subscriber::FmtSubscriber; #[tokio::main] @@ -60,7 +59,7 @@ async fn main() { .expect("No developer teams available for this account"); let res = dev_session - .list_devices(team, None) + .list_all_development_certs(team, None) .await .expect("Failed to list developer devices"); diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 7ed3adb..934529b 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -37,4 +37,5 @@ pbkdf2 = "0.12.2" hmac = "0.12.1" cbc = { version = "0.1.2", features = ["std"] } aes = "0.8.4" -aes-gcm = "0.10.3" \ No newline at end of file +aes-gcm = "0.10.3" +serde_bytes = "0.11.19" diff --git a/isideload/src/dev/developer_session.rs b/isideload/src/dev/developer_session.rs index ff1eb2a..5e46342 100644 --- a/isideload/src/dev/developer_session.rs +++ b/isideload/src/dev/developer_session.rs @@ -14,7 +14,8 @@ use crate::{ dev::structures::{ DeveloperDevice, DeveloperDeviceType::{self, *}, - DeveloperTeam, ListDevicesResponse, ListTeamsResponse, + DeveloperTeam, DevelopmentCertificate, ListCertificatesResponse, ListDevicesResponse, + ListTeamsResponse, }, util::plist::PlistDataExtract, }; @@ -136,6 +137,30 @@ impl<'a> DeveloperSession<'a> { Ok(response.devices) } + + pub async fn list_all_development_certs( + &self, + team: &DeveloperTeam, + device_type: impl Into>, + ) -> Result, Report> { + let body = plist!(dict { + "teamId": &team.team_id, + }); + + let response: ListCertificatesResponse = self + .send_developer_request(&dev_url("listAllDevelopmentCerts", device_type), body) + .await + .context("Failed to list development certificates")?; + + if response.result_code != 0 { + warn!( + "Non-zero list development certs response code: {}", + response.result_code + ) + }; + + Ok(response.certificates) + } } fn dev_url(endpoint: &str, device_type: impl Into>) -> String { diff --git a/isideload/src/dev/structures.rs b/isideload/src/dev/structures.rs index 39e8851..76e1ae0 100644 --- a/isideload/src/dev/structures.rs +++ b/isideload/src/dev/structures.rs @@ -1,4 +1,5 @@ use serde::Deserialize; +use serde_bytes::ByteBuf; #[derive(Debug, Clone)] pub enum DeveloperDeviceType { @@ -50,3 +51,20 @@ pub struct ListDevicesResponse { pub devices: Vec, pub result_code: i64, } + +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct DevelopmentCertificate { + pub name: String, + pub certificate_id: String, + pub serial_number: Option, + pub machine_id: Option, + pub cert_content: Option, +} + +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct ListCertificatesResponse { + pub certificates: Vec, + pub result_code: i64, +} From c0f02994ec6f3f9671b53229ce2159924e67f664 Mon Sep 17 00:00:00 2001 From: nab138 Date: Thu, 29 Jan 2026 20:51:13 -0500 Subject: [PATCH 28/71] Improve developer session layout --- examples/minimal/src/main.rs | 4 +- isideload/src/dev/developer_session.rs | 105 ++++++++++++++++--------- isideload/src/dev/structures.rs | 33 +++++--- isideload/src/util/plist.rs | 35 +++++++++ 4 files changed, 127 insertions(+), 50 deletions(-) diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index ae250d0..eb74f6f 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -59,9 +59,9 @@ async fn main() { .expect("No developer teams available for this account"); let res = dev_session - .list_all_development_certs(team, None) + .list_devices(team, None) .await .expect("Failed to list developer devices"); - println!("{:#?}", res); + println!("{:?}", res); } diff --git a/isideload/src/dev/developer_session.rs b/isideload/src/dev/developer_session.rs index 5e46342..4670b27 100644 --- a/isideload/src/dev/developer_session.rs +++ b/isideload/src/dev/developer_session.rs @@ -2,7 +2,7 @@ use plist::Dictionary; use plist_macro::{plist, plist_to_xml_string}; use rootcause::prelude::*; use serde::de::DeserializeOwned; -use tracing::warn; +use tracing::{error, warn}; use uuid::Uuid; use crate::{ @@ -12,10 +12,8 @@ use crate::{ grandslam::GrandSlam, }, dev::structures::{ - DeveloperDevice, DeveloperDeviceType::{self, *}, - DeveloperTeam, DevelopmentCertificate, ListCertificatesResponse, ListDevicesResponse, - ListTeamsResponse, + *, }, util::plist::PlistDataExtract, }; @@ -64,6 +62,7 @@ impl<'a> DeveloperSession<'a> { pub async fn send_developer_request( &self, url: &str, + result_key: &str, body: impl Into>, ) -> Result { let body = body.into().unwrap_or_else(|| Dictionary::new()); @@ -92,26 +91,51 @@ impl<'a> DeveloperSession<'a> { .await .context("Failed to read developer request response text")?; - let dict: T = plist::from_bytes(text.as_bytes()) + let dict: Dictionary = plist::from_bytes(text.as_bytes()) .context("Failed to parse developer request plist")?; - Ok(dict) + // All this error handling is here to ensure that: + // 1. We always warn/log errors from the server even if it returns the expected data + // 2. We return server errors if the expected data is missing + // 3. We return parsing errors if there is no server error but the expected data is missing + let response_code = dict.get("resultCode").and_then(|v| v.as_signed_integer()); + let mut server_error: Option = None; + if let Some(code) = response_code { + if code != 0 { + server_error = Some(format!( + "{} Code {}: {}", + dict.get("userString") + .and_then(|v| v.as_string()) + .unwrap_or("Developer request failed."), + code, + dict.get("resultString") + .and_then(|v| v.as_string()) + .unwrap_or("No error message given.") + )); + error!(server_error); + } + } else { + warn!("No resultCode in developer request response"); + } + + let result: Result = dict.get_struct(result_key); + + if let Err(_) = &result { + if let Some(err) = server_error { + bail!(err); + } + } + + Ok(result.context("Failed to extract developer request result")?) } pub async fn list_teams(&self) -> Result, Report> { - let response: ListTeamsResponse = self - .send_developer_request(&dev_url("listTeams", Any), None) + let response: Vec = self + .send_developer_request(&dev_url("listTeams", Any), "teams", None) .await .context("Failed to list developer teams")?; - if response.result_code != 0 { - warn!( - "Non-zero list teams response code: {}", - response.result_code - ) - }; - - Ok(response.teams) + Ok(response) } pub async fn list_devices( @@ -123,19 +147,33 @@ impl<'a> DeveloperSession<'a> { "teamId": &team.team_id, }); - let response: ListDevicesResponse = self - .send_developer_request(&dev_url("listDevices", device_type), body) + let devices: Vec = self + .send_developer_request(&dev_url("listDevices", device_type), "devices", body) .await .context("Failed to list developer devices")?; - if response.result_code != 0 { - warn!( - "Non-zero list devices response code: {}", - response.result_code - ) - }; + Ok(devices) + } - Ok(response.devices) + pub async fn add_device( + &self, + team: &DeveloperTeam, + name: &str, + udid: &str, + device_type: impl Into>, + ) -> Result { + let body = plist!(dict { + "teamId": &team.team_id, + "name": name, + "deviceNumber": udid, + }); + + let device: DeveloperDevice = self + .send_developer_request(&dev_url("addDevice", device_type), "device", body) + .await + .context("Failed to add developer device")?; + + Ok(device) } pub async fn list_all_development_certs( @@ -147,19 +185,16 @@ impl<'a> DeveloperSession<'a> { "teamId": &team.team_id, }); - let response: ListCertificatesResponse = self - .send_developer_request(&dev_url("listAllDevelopmentCerts", device_type), body) + let certs: Vec = self + .send_developer_request( + &dev_url("listAllDevelopmentCerts", device_type), + "certificates", + body, + ) .await .context("Failed to list development certificates")?; - if response.result_code != 0 { - warn!( - "Non-zero list development certs response code: {}", - response.result_code - ) - }; - - Ok(response.certificates) + Ok(certs) } } diff --git a/isideload/src/dev/structures.rs b/isideload/src/dev/structures.rs index 76e1ae0..5631f18 100644 --- a/isideload/src/dev/structures.rs +++ b/isideload/src/dev/structures.rs @@ -34,6 +34,7 @@ pub struct DeveloperTeam { pub struct ListTeamsResponse { pub teams: Vec, pub result_code: i64, + pub result_string: Option, } #[derive(Deserialize, Debug, Clone)] @@ -45,14 +46,7 @@ pub struct DeveloperDevice { pub status: Option, } -#[derive(Deserialize, Debug, Clone)] -#[serde(rename_all = "camelCase")] -pub struct ListDevicesResponse { - pub devices: Vec, - pub result_code: i64, -} - -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct DevelopmentCertificate { pub name: String, @@ -62,9 +56,22 @@ pub struct DevelopmentCertificate { pub cert_content: Option, } -#[derive(Deserialize, Debug, Clone)] -#[serde(rename_all = "camelCase")] -pub struct ListCertificatesResponse { - pub certificates: Vec, - pub result_code: i64, +// the automatic debug implementation spams the console with the cert content bytes +impl std::fmt::Debug for DevelopmentCertificate { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DevelopmentCertificate") + .field("name", &self.name) + .field("certificate_id", &self.certificate_id) + .field("serial_number", &self.serial_number) + .field("machine_id", &self.machine_id) + .field( + "cert_content", + &self + .cert_content + .as_ref() + .map(|c| format!("Some([{} bytes])", c.len())) + .unwrap_or("None".to_string()), + ) + .finish() + } } diff --git a/isideload/src/util/plist.rs b/isideload/src/util/plist.rs index c02e0bc..be54ecf 100644 --- a/isideload/src/util/plist.rs +++ b/isideload/src/util/plist.rs @@ -1,6 +1,8 @@ use plist::Dictionary; use plist_macro::pretty_print_dictionary; use rootcause::prelude::*; +use serde::de::DeserializeOwned; +use tracing::error; pub struct SensitivePlistAttachment { pub plist: Dictionary, @@ -11,6 +13,18 @@ impl SensitivePlistAttachment { SensitivePlistAttachment { plist } } + pub fn from_text(text: &str) -> Self { + let dict: Result = plist::from_bytes(text.as_bytes()); + if let Err(e) = &dict { + error!( + "Failed to parse plist text for sensitive attachment, returning empty plist: {:?}", + e + ); + return SensitivePlistAttachment::new(Dictionary::new()); + } + SensitivePlistAttachment::new(dict.unwrap()) + } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // if env variable DEBUG_SENSITIVE is set, print full plist if std::env::var("DEBUG_SENSITIVE").is_ok() { @@ -41,6 +55,7 @@ pub trait PlistDataExtract { fn get_string(&self, key: &str) -> Result; fn get_signed_integer(&self, key: &str) -> Result; fn get_dict(&self, key: &str) -> Result<&Dictionary, Report>; + fn get_struct(&self, key: &str) -> Result; } impl PlistDataExtract for Dictionary { @@ -85,4 +100,24 @@ impl PlistDataExtract for Dictionary { .attach(SensitivePlistAttachment::new(self.clone())) }) } + + fn get_struct(&self, key: &str) -> Result { + let dict = self.get(key); + if dict.is_none() { + return Err(report!("Plist missing dictionary for key '{}'", key) + .attach(SensitivePlistAttachment::new(self.clone()))); + } + let dict = dict.unwrap(); + let struct_data: T = plist::from_value(dict).map_err(|e| { + report!( + "Failed to deserialize plist struct for key '{}': {:?}", + key, + e + ) + .attach(SensitivePlistAttachment::new( + dict.as_dictionary().cloned().unwrap_or_default(), + )) + })?; + Ok(struct_data) + } } From 6fd088eb65213fcece1b5356d6db234c4b66c275 Mon Sep 17 00:00:00 2001 From: nab138 Date: Thu, 29 Jan 2026 22:29:41 -0500 Subject: [PATCH 29/71] broken --- examples/minimal/src/main.rs | 4 +- isideload/src/dev/developer_session.rs | 140 +++++++++++++++++++++---- isideload/src/dev/structures.rs | 33 +++++- 3 files changed, 150 insertions(+), 27 deletions(-) diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index eb74f6f..fc6bdc9 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -40,7 +40,7 @@ async fn main() { match &account { Ok(a) => println!("Logged in. {}", a), - Err(e) => eprintln!("Failed to log in to Apple ID: {:?}", e), + Err(e) => panic!("Failed to log in to Apple ID: {:?}", e), } let mut account = account.unwrap(); @@ -59,7 +59,7 @@ async fn main() { .expect("No developer teams available for this account"); let res = dev_session - .list_devices(team, None) + .revoke_development_cert(team, "2655CFC31A258B1B4D7D9FC22E23AEC3", None) .await .expect("Failed to list developer devices"); diff --git a/isideload/src/dev/developer_session.rs b/isideload/src/dev/developer_session.rs index 4670b27..d9a613b 100644 --- a/isideload/src/dev/developer_session.rs +++ b/isideload/src/dev/developer_session.rs @@ -1,4 +1,4 @@ -use plist::Dictionary; +use plist::{Dictionary, Value}; use plist_macro::{plist, plist_to_xml_string}; use rootcause::prelude::*; use serde::de::DeserializeOwned; @@ -15,7 +15,7 @@ use crate::{ DeveloperDeviceType::{self, *}, *, }, - util::plist::PlistDataExtract, + util::plist::{PlistDataExtract, SensitivePlistAttachment}, }; pub struct DeveloperSession<'a> { @@ -59,12 +59,11 @@ impl<'a> DeveloperSession<'a> { )) } - pub async fn send_developer_request( + async fn send_dev_request_internal( &self, url: &str, - result_key: &str, body: impl Into>, - ) -> Result { + ) -> Result<(Dictionary, Option), Report> { let body = body.into().unwrap_or_else(|| Dictionary::new()); let base = plist!(dict { @@ -102,23 +101,41 @@ impl<'a> DeveloperSession<'a> { let mut server_error: Option = None; if let Some(code) = response_code { if code != 0 { - server_error = Some(format!( - "{} Code {}: {}", - dict.get("userString") - .and_then(|v| v.as_string()) - .unwrap_or("Developer request failed."), - code, - dict.get("resultString") - .and_then(|v| v.as_string()) - .unwrap_or("No error message given.") - )); + let user_string = dict + .get("userString") + .and_then(|v| v.as_string()) + .unwrap_or("Developer request failed."); + + let result_string = dict + .get("resultString") + .and_then(|v| v.as_string()) + .unwrap_or("No error message given."); + + // if user and result string match, only show one + if user_string == result_string { + server_error = Some(format!("{} Code: {}", user_string, code)); + } else { + server_error = + Some(format!("{} Code: {}; {}", user_string, code, result_string)); + } error!(server_error); } } else { warn!("No resultCode in developer request response"); } - let result: Result = dict.get_struct(result_key); + Ok((dict, server_error)) + } + + pub async fn send_dev_request( + &self, + url: &str, + body: impl Into>, + response_key: &str, + ) -> Result { + let (dict, server_error) = self.send_dev_request_internal(url, body).await?; + + let result: Result = dict.get_struct(response_key); if let Err(_) = &result { if let Some(err) = server_error { @@ -129,9 +146,23 @@ impl<'a> DeveloperSession<'a> { Ok(result.context("Failed to extract developer request result")?) } + pub async fn send_dev_request_no_response( + &self, + url: &str, + body: impl Into>, + ) -> Result { + let (dict, server_error) = self.send_dev_request_internal(url, body).await?; + + if let Some(err) = server_error { + bail!(err); + } + + Ok(dict) + } + pub async fn list_teams(&self) -> Result, Report> { let response: Vec = self - .send_developer_request(&dev_url("listTeams", Any), "teams", None) + .send_dev_request(&dev_url("listTeams", Any), None, "teams") .await .context("Failed to list developer teams")?; @@ -148,7 +179,7 @@ impl<'a> DeveloperSession<'a> { }); let devices: Vec = self - .send_developer_request(&dev_url("listDevices", device_type), "devices", body) + .send_dev_request(&dev_url("listDevices", device_type), body, "devices") .await .context("Failed to list developer devices")?; @@ -169,7 +200,7 @@ impl<'a> DeveloperSession<'a> { }); let device: DeveloperDevice = self - .send_developer_request(&dev_url("addDevice", device_type), "device", body) + .send_dev_request(&dev_url("addDevice", device_type), body, "device") .await .context("Failed to add developer device")?; @@ -186,16 +217,83 @@ impl<'a> DeveloperSession<'a> { }); let certs: Vec = self - .send_developer_request( + .send_dev_request( &dev_url("listAllDevelopmentCerts", device_type), - "certificates", body, + "certificates", ) .await .context("Failed to list development certificates")?; Ok(certs) } + + pub async fn revoke_development_cert( + &self, + team: &DeveloperTeam, + serial_number: &str, + device_type: impl Into>, + ) -> Result<(), Report> { + let body = plist!(dict { + "teamId": &team.team_id, + "serialNumber": serial_number, + }); + + self.send_dev_request_no_response( + &dev_url("revokeDevelopmentCert", device_type), + Some(body), + ) + .await + .context("Failed to revoke development certificate")?; + + Ok(()) + } + + pub async fn submit_development_csr( + &self, + team: &DeveloperTeam, + csr_content: String, + machine_name: String, + device_type: impl Into>, + ) -> Result { + let body = plist!(dict { + "teamId": &team.team_id, + "csrContent": csr_content, + "machineName": machine_name, + "machineId": Uuid::new_v4().to_string().to_uppercase(), + }); + + let cert: CertRequest = self + .send_dev_request( + &dev_url("submitDevelopmentCSR", device_type), + body, + "certRequest", + ) + .await + .context("Failed to submit development CSR")?; + + Ok(cert) + } + + pub async fn list_app_ids(&self, team: &DeveloperTeam) -> Result { + let body = plist!(dict { + "teamId": &team.team_id, + }); + + let response: Value = self + .send_dev_request_no_response(&dev_url("listAppIds", Any), body) + .await + .context("Failed to list developer app IDs")? + .into(); + + let app_ids: ListAppIdsResponse = plist::from_value(&response).map_err(|e| { + report!("Failed to deserialize app id response: {:?}", e).attach( + SensitivePlistAttachment::new(response.as_dictionary().clone().unwrap_or_default()), + ) + })?; + + Ok(app_ids) + } } fn dev_url(endpoint: &str, device_type: impl Into>) -> String { diff --git a/isideload/src/dev/structures.rs b/isideload/src/dev/structures.rs index 5631f18..fa7703d 100644 --- a/isideload/src/dev/structures.rs +++ b/isideload/src/dev/structures.rs @@ -1,3 +1,4 @@ +use plist::{Date, Dictionary}; use serde::Deserialize; use serde_bytes::ByteBuf; @@ -40,8 +41,8 @@ pub struct ListTeamsResponse { #[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct DeveloperDevice { - pub name: String, - pub device_id: String, + pub name: Option, + pub device_id: Option, pub device_number: String, pub status: Option, } @@ -49,8 +50,8 @@ pub struct DeveloperDevice { #[derive(Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct DevelopmentCertificate { - pub name: String, - pub certificate_id: String, + pub name: Option, + pub certificate_id: Option, pub serial_number: Option, pub machine_id: Option, pub cert_content: Option, @@ -75,3 +76,27 @@ impl std::fmt::Debug for DevelopmentCertificate { .finish() } } + +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct CertRequest { + pub cert_request_id: String, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AppId { + pub app_id_id: String, + pub identifier: String, + pub name: String, + pub features: Option, + pub expiration_date: Option, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ListAppIdsResponse { + pub app_ids: Vec, + pub max_quantity: Option, + pub available_quantity: Option, +} From 8f05fecf0f2fac794144164294731f0d50c772a0 Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 30 Jan 2026 09:36:09 -0500 Subject: [PATCH 30/71] Seperate dev APIs into traits and impliment some app id apis --- examples/minimal/src/main.rs | 9 +- isideload/src/dev/app_ids.rs | 93 ++++++++++++++ isideload/src/dev/certificates.rs | 128 ++++++++++++++++++++ isideload/src/dev/developer_session.rs | 160 ++----------------------- isideload/src/dev/device_type.rs | 29 +++++ isideload/src/dev/devices.rs | 68 +++++++++++ isideload/src/dev/mod.rs | 6 +- isideload/src/dev/structures.rs | 102 ---------------- isideload/src/dev/teams.rs | 36 ++++++ 9 files changed, 372 insertions(+), 259 deletions(-) create mode 100644 isideload/src/dev/app_ids.rs create mode 100644 isideload/src/dev/certificates.rs create mode 100644 isideload/src/dev/device_type.rs create mode 100644 isideload/src/dev/devices.rs delete mode 100644 isideload/src/dev/structures.rs create mode 100644 isideload/src/dev/teams.rs diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index fc6bdc9..dd7be2a 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -1,8 +1,9 @@ use std::env; use isideload::{ - anisette::remote_v3::RemoteV3AnisetteProvider, auth::apple_account::AppleAccount, - dev::developer_session::DeveloperSession, + anisette::remote_v3::RemoteV3AnisetteProvider, + auth::apple_account::AppleAccount, + dev::developer_session::{AppIdsApi, DeveloperSession, TeamsApi}, }; use tracing::Level; @@ -59,9 +60,9 @@ async fn main() { .expect("No developer teams available for this account"); let res = dev_session - .revoke_development_cert(team, "2655CFC31A258B1B4D7D9FC22E23AEC3", None) + .list_app_ids(team, None) .await - .expect("Failed to list developer devices"); + .expect("Failed to add appid"); println!("{:?}", res); } diff --git a/isideload/src/dev/app_ids.rs b/isideload/src/dev/app_ids.rs new file mode 100644 index 0000000..bc0ee0e --- /dev/null +++ b/isideload/src/dev/app_ids.rs @@ -0,0 +1,93 @@ +use crate::{ + dev::{ + developer_session::DeveloperSession, + device_type::{DeveloperDeviceType, dev_url}, + teams::DeveloperTeam, + }, + util::plist::SensitivePlistAttachment, +}; +use plist::{Date, Dictionary, Value}; +use plist_macro::plist; +use rootcause::prelude::*; +use serde::Deserialize; + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AppId { + pub app_id_id: String, + pub identifier: String, + pub name: String, + pub features: Option, + pub expiration_date: Option, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ListAppIdsResponse { + pub app_ids: Vec, + pub max_quantity: Option, + pub available_quantity: Option, +} + +#[async_trait::async_trait] +pub trait AppIdsApi { + fn developer_session(&self) -> &DeveloperSession<'_>; + + async fn add_app_id( + &self, + team: &DeveloperTeam, + name: &str, + identifier: &str, + device_type: impl Into> + Send, + ) -> Result { + let body = plist!(dict { + "teamId": &team.team_id, + "identifier": identifier, + "name": name, + }); + + let app_id: AppId = self + .developer_session() + .send_dev_request(&dev_url("addAppId", device_type), body, "appId") + .await + .context("Failed to add developer app ID")?; + + Ok(app_id) + } + + async fn list_app_ids( + &self, + team: &DeveloperTeam, + device_type: impl Into> + Send, + ) -> Result { + let body = plist!(dict { + "teamId": &team.team_id, + }); + + let response: Value = self + .developer_session() + .send_dev_request_no_response(&dev_url("listAppIds", device_type), body) + .await + .context("Failed to list developer app IDs")? + .into(); + + let app_ids: ListAppIdsResponse = plist::from_value(&response).map_err(|e| { + report!("Failed to deserialize app id response: {:?}", e).attach( + SensitivePlistAttachment::new( + response + .as_dictionary() + .unwrap_or(&Dictionary::new()) + .clone(), + ), + ) + })?; + + Ok(app_ids) + } +} + +impl AppIdsApi for DeveloperSession<'_> { + fn developer_session(&self) -> &DeveloperSession<'_> { + self + } +} diff --git a/isideload/src/dev/certificates.rs b/isideload/src/dev/certificates.rs new file mode 100644 index 0000000..bc434e5 --- /dev/null +++ b/isideload/src/dev/certificates.rs @@ -0,0 +1,128 @@ +use crate::dev::{ + developer_session::DeveloperSession, + device_type::{DeveloperDeviceType, dev_url}, + teams::DeveloperTeam, +}; +use plist_macro::plist; +use rootcause::prelude::*; +use serde::Deserialize; +use serde_bytes::ByteBuf; +use uuid::Uuid; + +#[derive(Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct DevelopmentCertificate { + pub name: Option, + pub certificate_id: Option, + pub serial_number: Option, + pub machine_id: Option, + pub cert_content: Option, +} + +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct CertRequest { + pub cert_request_id: String, +} + +// the automatic debug implementation spams the console with the cert content bytes +impl std::fmt::Debug for DevelopmentCertificate { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DevelopmentCertificate") + .field("name", &self.name) + .field("certificate_id", &self.certificate_id) + .field("serial_number", &self.serial_number) + .field("machine_id", &self.machine_id) + .field( + "cert_content", + &self + .cert_content + .as_ref() + .map(|c| format!("Some([{} bytes])", c.len())) + .unwrap_or("None".to_string()), + ) + .finish() + } +} + +#[async_trait::async_trait] +pub trait CertificatesApi { + fn developer_session(&self) -> &DeveloperSession<'_>; + + async fn list_all_development_certs( + &self, + team: &DeveloperTeam, + device_type: impl Into> + Send, + ) -> Result, Report> { + let body = plist!(dict { + "teamId": &team.team_id, + }); + + let certs: Vec = self + .developer_session() + .send_dev_request( + &dev_url("listAllDevelopmentCerts", device_type), + body, + "certificates", + ) + .await + .context("Failed to list development certificates")?; + + Ok(certs) + } + + async fn revoke_development_cert( + &self, + team: &DeveloperTeam, + serial_number: &str, + device_type: impl Into> + Send, + ) -> Result<(), Report> { + let body = plist!(dict { + "teamId": &team.team_id, + "serialNumber": serial_number, + }); + + self.developer_session() + .send_dev_request_no_response( + &dev_url("revokeDevelopmentCert", device_type), + Some(body), + ) + .await + .context("Failed to revoke development certificate")?; + + Ok(()) + } + + async fn submit_development_csr( + &self, + team: &DeveloperTeam, + csr_content: String, + machine_name: String, + device_type: impl Into> + Send, + ) -> Result { + let body = plist!(dict { + "teamId": &team.team_id, + "csrContent": csr_content, + "machineName": machine_name, + "machineId": Uuid::new_v4().to_string().to_uppercase(), + }); + + let cert: CertRequest = self + .developer_session() + .send_dev_request( + &dev_url("submitDevelopmentCSR", device_type), + body, + "certRequest", + ) + .await + .context("Failed to submit development CSR")?; + + Ok(cert) + } +} + +impl CertificatesApi for DeveloperSession<'_> { + fn developer_session(&self) -> &DeveloperSession<'_> { + self + } +} diff --git a/isideload/src/dev/developer_session.rs b/isideload/src/dev/developer_session.rs index d9a613b..f279791 100644 --- a/isideload/src/dev/developer_session.rs +++ b/isideload/src/dev/developer_session.rs @@ -1,4 +1,4 @@ -use plist::{Dictionary, Value}; +use plist::Dictionary; use plist_macro::{plist, plist_to_xml_string}; use rootcause::prelude::*; use serde::de::DeserializeOwned; @@ -11,13 +11,15 @@ use crate::{ apple_account::{AppToken, AppleAccount}, grandslam::GrandSlam, }, - dev::structures::{ - DeveloperDeviceType::{self, *}, - *, - }, - util::plist::{PlistDataExtract, SensitivePlistAttachment}, + util::plist::PlistDataExtract, }; +pub use super::app_ids::*; +pub use super::certificates::*; +pub use super::device_type::DeveloperDeviceType; +pub use super::devices::*; +pub use super::teams::*; + pub struct DeveloperSession<'a> { token: AppToken, adsid: String, @@ -159,150 +161,4 @@ impl<'a> DeveloperSession<'a> { Ok(dict) } - - pub async fn list_teams(&self) -> Result, Report> { - let response: Vec = self - .send_dev_request(&dev_url("listTeams", Any), None, "teams") - .await - .context("Failed to list developer teams")?; - - Ok(response) - } - - pub async fn list_devices( - &self, - team: &DeveloperTeam, - device_type: impl Into>, - ) -> Result, Report> { - let body = plist!(dict { - "teamId": &team.team_id, - }); - - let devices: Vec = self - .send_dev_request(&dev_url("listDevices", device_type), body, "devices") - .await - .context("Failed to list developer devices")?; - - Ok(devices) - } - - pub async fn add_device( - &self, - team: &DeveloperTeam, - name: &str, - udid: &str, - device_type: impl Into>, - ) -> Result { - let body = plist!(dict { - "teamId": &team.team_id, - "name": name, - "deviceNumber": udid, - }); - - let device: DeveloperDevice = self - .send_dev_request(&dev_url("addDevice", device_type), body, "device") - .await - .context("Failed to add developer device")?; - - Ok(device) - } - - pub async fn list_all_development_certs( - &self, - team: &DeveloperTeam, - device_type: impl Into>, - ) -> Result, Report> { - let body = plist!(dict { - "teamId": &team.team_id, - }); - - let certs: Vec = self - .send_dev_request( - &dev_url("listAllDevelopmentCerts", device_type), - body, - "certificates", - ) - .await - .context("Failed to list development certificates")?; - - Ok(certs) - } - - pub async fn revoke_development_cert( - &self, - team: &DeveloperTeam, - serial_number: &str, - device_type: impl Into>, - ) -> Result<(), Report> { - let body = plist!(dict { - "teamId": &team.team_id, - "serialNumber": serial_number, - }); - - self.send_dev_request_no_response( - &dev_url("revokeDevelopmentCert", device_type), - Some(body), - ) - .await - .context("Failed to revoke development certificate")?; - - Ok(()) - } - - pub async fn submit_development_csr( - &self, - team: &DeveloperTeam, - csr_content: String, - machine_name: String, - device_type: impl Into>, - ) -> Result { - let body = plist!(dict { - "teamId": &team.team_id, - "csrContent": csr_content, - "machineName": machine_name, - "machineId": Uuid::new_v4().to_string().to_uppercase(), - }); - - let cert: CertRequest = self - .send_dev_request( - &dev_url("submitDevelopmentCSR", device_type), - body, - "certRequest", - ) - .await - .context("Failed to submit development CSR")?; - - Ok(cert) - } - - pub async fn list_app_ids(&self, team: &DeveloperTeam) -> Result { - let body = plist!(dict { - "teamId": &team.team_id, - }); - - let response: Value = self - .send_dev_request_no_response(&dev_url("listAppIds", Any), body) - .await - .context("Failed to list developer app IDs")? - .into(); - - let app_ids: ListAppIdsResponse = plist::from_value(&response).map_err(|e| { - report!("Failed to deserialize app id response: {:?}", e).attach( - SensitivePlistAttachment::new(response.as_dictionary().clone().unwrap_or_default()), - ) - })?; - - Ok(app_ids) - } -} - -fn dev_url(endpoint: &str, device_type: impl Into>) -> String { - format!( - "https://developerservices2.apple.com/services/QH65B2/{}{}.action?clientId=XABBG36SBA", - device_type - .into() - .unwrap_or(DeveloperDeviceType::Ios) - .url_segment(), - endpoint, - ) } diff --git a/isideload/src/dev/device_type.rs b/isideload/src/dev/device_type.rs new file mode 100644 index 0000000..d2a1001 --- /dev/null +++ b/isideload/src/dev/device_type.rs @@ -0,0 +1,29 @@ +#[derive(Debug, Clone)] +pub enum DeveloperDeviceType { + Any, + Ios, + Tvos, + Watchos, +} + +impl DeveloperDeviceType { + pub fn url_segment(&self) -> &'static str { + match self { + DeveloperDeviceType::Any => "", + DeveloperDeviceType::Ios => "ios/", + DeveloperDeviceType::Tvos => "tvos/", + DeveloperDeviceType::Watchos => "watchos/", + } + } +} + +pub fn dev_url(endpoint: &str, device_type: impl Into>) -> String { + format!( + "https://developerservices2.apple.com/services/QH65B2/{}{}.action?clientId=XABBG36SBA", + device_type + .into() + .unwrap_or(DeveloperDeviceType::Ios) + .url_segment(), + endpoint, + ) +} diff --git a/isideload/src/dev/devices.rs b/isideload/src/dev/devices.rs new file mode 100644 index 0000000..860f371 --- /dev/null +++ b/isideload/src/dev/devices.rs @@ -0,0 +1,68 @@ +use crate::dev::{ + developer_session::DeveloperSession, + device_type::{DeveloperDeviceType, dev_url}, + teams::DeveloperTeam, +}; +use plist_macro::plist; +use rootcause::prelude::*; +use serde::Deserialize; + +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct DeveloperDevice { + pub name: Option, + pub device_id: Option, + pub device_number: String, + pub status: Option, +} + +#[async_trait::async_trait] +pub trait DevicesApi { + fn developer_session(&self) -> &DeveloperSession<'_>; + + async fn list_devices( + &self, + team: &DeveloperTeam, + device_type: impl Into> + Send, + ) -> Result, Report> { + let body = plist!(dict { + "teamId": &team.team_id, + }); + + let devices: Vec = self + .developer_session() + .send_dev_request(&dev_url("listDevices", device_type), body, "devices") + .await + .context("Failed to list developer devices")?; + + Ok(devices) + } + + async fn add_device( + &self, + team: &DeveloperTeam, + name: &str, + udid: &str, + device_type: impl Into> + Send, + ) -> Result { + let body = plist!(dict { + "teamId": &team.team_id, + "name": name, + "deviceNumber": udid, + }); + + let device: DeveloperDevice = self + .developer_session() + .send_dev_request(&dev_url("addDevice", device_type), body, "device") + .await + .context("Failed to add developer device")?; + + Ok(device) + } +} + +impl DevicesApi for DeveloperSession<'_> { + fn developer_session(&self) -> &DeveloperSession<'_> { + self + } +} diff --git a/isideload/src/dev/mod.rs b/isideload/src/dev/mod.rs index 1a66810..1f8b80a 100644 --- a/isideload/src/dev/mod.rs +++ b/isideload/src/dev/mod.rs @@ -1,2 +1,6 @@ +pub mod app_ids; +pub mod certificates; pub mod developer_session; -pub mod structures; +pub mod device_type; +pub mod devices; +pub mod teams; diff --git a/isideload/src/dev/structures.rs b/isideload/src/dev/structures.rs deleted file mode 100644 index fa7703d..0000000 --- a/isideload/src/dev/structures.rs +++ /dev/null @@ -1,102 +0,0 @@ -use plist::{Date, Dictionary}; -use serde::Deserialize; -use serde_bytes::ByteBuf; - -#[derive(Debug, Clone)] -pub enum DeveloperDeviceType { - Any, - Ios, - Tvos, - Watchos, -} - -impl DeveloperDeviceType { - pub fn url_segment(&self) -> &'static str { - match self { - DeveloperDeviceType::Any => "", - DeveloperDeviceType::Ios => "ios/", - DeveloperDeviceType::Tvos => "tvos/", - DeveloperDeviceType::Watchos => "watchos/", - } - } -} - -#[derive(Deserialize, Debug, Clone)] -#[serde(rename_all = "camelCase")] -pub struct DeveloperTeam { - pub name: Option, - pub team_id: String, - pub r#type: Option, - pub status: Option, -} - -#[derive(Deserialize, Debug, Clone)] -#[serde(rename_all = "camelCase")] -pub struct ListTeamsResponse { - pub teams: Vec, - pub result_code: i64, - pub result_string: Option, -} - -#[derive(Deserialize, Debug, Clone)] -#[serde(rename_all = "camelCase")] -pub struct DeveloperDevice { - pub name: Option, - pub device_id: Option, - pub device_number: String, - pub status: Option, -} - -#[derive(Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct DevelopmentCertificate { - pub name: Option, - pub certificate_id: Option, - pub serial_number: Option, - pub machine_id: Option, - pub cert_content: Option, -} - -// the automatic debug implementation spams the console with the cert content bytes -impl std::fmt::Debug for DevelopmentCertificate { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("DevelopmentCertificate") - .field("name", &self.name) - .field("certificate_id", &self.certificate_id) - .field("serial_number", &self.serial_number) - .field("machine_id", &self.machine_id) - .field( - "cert_content", - &self - .cert_content - .as_ref() - .map(|c| format!("Some([{} bytes])", c.len())) - .unwrap_or("None".to_string()), - ) - .finish() - } -} - -#[derive(Deserialize, Debug, Clone)] -#[serde(rename_all = "camelCase")] -pub struct CertRequest { - pub cert_request_id: String, -} - -#[derive(Debug, Clone, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct AppId { - pub app_id_id: String, - pub identifier: String, - pub name: String, - pub features: Option, - pub expiration_date: Option, -} - -#[derive(Debug, Clone, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct ListAppIdsResponse { - pub app_ids: Vec, - pub max_quantity: Option, - pub available_quantity: Option, -} diff --git a/isideload/src/dev/teams.rs b/isideload/src/dev/teams.rs new file mode 100644 index 0000000..5bf4b98 --- /dev/null +++ b/isideload/src/dev/teams.rs @@ -0,0 +1,36 @@ +use crate::dev::{ + developer_session::DeveloperSession, + device_type::{DeveloperDeviceType::*, dev_url}, +}; +use rootcause::prelude::*; +use serde::Deserialize; + +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct DeveloperTeam { + pub name: Option, + pub team_id: String, + pub r#type: Option, + pub status: Option, +} + +#[async_trait::async_trait] +pub trait TeamsApi { + fn developer_session(&self) -> &DeveloperSession<'_>; + + async fn list_teams(&self) -> Result, Report> { + let response: Vec = self + .developer_session() + .send_dev_request(&dev_url("listTeams", Any), None, "teams") + .await + .context("Failed to list developer teams")?; + + Ok(response) + } +} + +impl TeamsApi for DeveloperSession<'_> { + fn developer_session(&self) -> &DeveloperSession<'_> { + self + } +} From 055478061813c7039de4712c9101ab7a852fabfa Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 30 Jan 2026 12:12:48 -0500 Subject: [PATCH 31/71] finish app ids api --- isideload/src/dev/app_ids.rs | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/isideload/src/dev/app_ids.rs b/isideload/src/dev/app_ids.rs index bc0ee0e..baf68a1 100644 --- a/isideload/src/dev/app_ids.rs +++ b/isideload/src/dev/app_ids.rs @@ -84,6 +84,48 @@ pub trait AppIdsApi { Ok(app_ids) } + + async fn update_app_id( + &self, + team: &DeveloperTeam, + app_id: &AppId, + features: &Dictionary, + device_type: impl Into> + Send, + ) -> Result { + let mut body = plist!(dict { + "teamId": &team.team_id, + "appIdId": &app_id.app_id_id + }); + + for (key, value) in features { + body.insert(key.clone(), value.clone()); + } + + Ok(self + .developer_session() + .send_dev_request(&dev_url("updateAppId", device_type), body, "appId") + .await + .context("Failed to update developer app ID")?) + } + + async fn delete_app_id( + &self, + team: &DeveloperTeam, + app_id: &AppId, + device_type: impl Into> + Send, + ) -> Result<(), Report> { + let body = plist!(dict { + "teamId": &team.team_id, + "appIdId": &app_id.app_id_id, + }); + + self.developer_session() + .send_dev_request_no_response(&dev_url("deleteAppId", device_type), body) + .await + .context("Failed to delete developer app ID")?; + + Ok(()) + } } impl AppIdsApi for DeveloperSession<'_> { From 1648537bd08b2b71c547f3a5c2fa9d6ba3664f6b Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 30 Jan 2026 13:56:50 -0500 Subject: [PATCH 32/71] Impliment some of app groups --- examples/minimal/src/main.rs | 4 +- isideload/src/dev/app_groups.rs | 79 ++++++++++++++++++++++++++ isideload/src/dev/developer_session.rs | 1 + isideload/src/dev/mod.rs | 1 + 4 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 isideload/src/dev/app_groups.rs diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index dd7be2a..61eeab0 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -3,7 +3,7 @@ use std::env; use isideload::{ anisette::remote_v3::RemoteV3AnisetteProvider, auth::apple_account::AppleAccount, - dev::developer_session::{AppIdsApi, DeveloperSession, TeamsApi}, + dev::developer_session::{AppGroupsApi, DeveloperSession, TeamsApi}, }; use tracing::Level; @@ -60,7 +60,7 @@ async fn main() { .expect("No developer teams available for this account"); let res = dev_session - .list_app_ids(team, None) + .add_app_group(team, "Example", "group.me.nabdev.example.59AV98CNR7", None) .await .expect("Failed to add appid"); diff --git a/isideload/src/dev/app_groups.rs b/isideload/src/dev/app_groups.rs new file mode 100644 index 0000000..e3cc176 --- /dev/null +++ b/isideload/src/dev/app_groups.rs @@ -0,0 +1,79 @@ +use crate::{ + dev::{ + developer_session::DeveloperSession, + device_type::{DeveloperDeviceType, dev_url}, + teams::DeveloperTeam, + }, + util::plist::SensitivePlistAttachment, +}; +use plist::{Date, Dictionary, Value}; +use plist_macro::plist; +use rootcause::prelude::*; +use serde::Deserialize; + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AppGroup { + pub name: Option, + pub identifier: String, + pub application_group: String, +} + +#[async_trait::async_trait] +pub trait AppGroupsApi { + fn developer_session(&self) -> &DeveloperSession<'_>; + + async fn list_app_groups( + &self, + team: &DeveloperTeam, + device_type: impl Into> + Send, + ) -> Result, Report> { + let body = plist!(dict { + "teamId": &team.team_id, + }); + + let app_groups: Vec = self + .developer_session() + .send_dev_request( + &dev_url("listApplicationGroups", device_type), + body, + "applicationGroupList", + ) + .await + .context("Failed to list developer app groups")?; + + Ok(app_groups) + } + + async fn add_app_group( + &self, + team: &DeveloperTeam, + name: &str, + identifier: &str, + device_type: impl Into> + Send, + ) -> Result { + let body = plist!(dict { + "teamId": &team.team_id, + "name": name, + "identifier": identifier, + }); + + let app_group: AppGroup = self + .developer_session() + .send_dev_request( + &dev_url("addApplicationGroup", device_type), + body, + "applicationGroup", + ) + .await + .context("Failed to add developer app group")?; + + Ok(app_group) + } +} + +impl AppGroupsApi for DeveloperSession<'_> { + fn developer_session(&self) -> &DeveloperSession<'_> { + self + } +} diff --git a/isideload/src/dev/developer_session.rs b/isideload/src/dev/developer_session.rs index f279791..1ecce74 100644 --- a/isideload/src/dev/developer_session.rs +++ b/isideload/src/dev/developer_session.rs @@ -14,6 +14,7 @@ use crate::{ util::plist::PlistDataExtract, }; +pub use super::app_groups::*; pub use super::app_ids::*; pub use super::certificates::*; pub use super::device_type::DeveloperDeviceType; diff --git a/isideload/src/dev/mod.rs b/isideload/src/dev/mod.rs index 1f8b80a..e4e7a16 100644 --- a/isideload/src/dev/mod.rs +++ b/isideload/src/dev/mod.rs @@ -1,3 +1,4 @@ +pub mod app_groups; pub mod app_ids; pub mod certificates; pub mod developer_session; From 80121a0b913ade0b9f9706f6f16ac67c26617ef2 Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 30 Jan 2026 18:21:42 -0500 Subject: [PATCH 33/71] Impliment provisioning profiles and assigning app groups --- Cargo.lock | 11 ------- examples/minimal/src/main.rs | 15 ++++++++-- isideload/Cargo.toml | 1 - isideload/src/dev/app_groups.rs | 37 ++++++++++++++++++----- isideload/src/dev/app_ids.rs | 49 +++++++++++++++++++++++++++++-- isideload/src/dev/certificates.rs | 6 ++-- 6 files changed, 91 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2926664..b72407b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -821,7 +821,6 @@ dependencies = [ "reqwest", "rootcause", "serde", - "serde_bytes", "serde_json", "sha2", "thiserror 2.0.18", @@ -1452,16 +1451,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde_bytes" -version = "0.11.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" -dependencies = [ - "serde", - "serde_core", -] - [[package]] name = "serde_core" version = "1.0.228" diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 61eeab0..a68aeb3 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -3,7 +3,10 @@ use std::env; use isideload::{ anisette::remote_v3::RemoteV3AnisetteProvider, auth::apple_account::AppleAccount, - dev::developer_session::{AppGroupsApi, DeveloperSession, TeamsApi}, + dev::{ + certificates::CertificatesApi, + developer_session::{DeveloperSession, TeamsApi}, + }, }; use tracing::Level; @@ -59,10 +62,16 @@ async fn main() { .get(0) .expect("No developer teams available for this account"); + // let app_ids = dev_session + // .list_app_ids(team, None) + // .await + // .expect("Failed to add appid"); + // let app_id = app_ids.app_ids.get(0).cloned().unwrap(); + let res = dev_session - .add_app_group(team, "Example", "group.me.nabdev.example.59AV98CNR7", None) + .list_all_development_certs(team, None) .await - .expect("Failed to add appid"); + .expect("Failed to list dev certs"); println!("{:?}", res); } diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 934529b..657d926 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -38,4 +38,3 @@ hmac = "0.12.1" cbc = { version = "0.1.2", features = ["std"] } aes = "0.8.4" aes-gcm = "0.10.3" -serde_bytes = "0.11.19" diff --git a/isideload/src/dev/app_groups.rs b/isideload/src/dev/app_groups.rs index e3cc176..2d0f5f9 100644 --- a/isideload/src/dev/app_groups.rs +++ b/isideload/src/dev/app_groups.rs @@ -1,12 +1,9 @@ -use crate::{ - dev::{ - developer_session::DeveloperSession, - device_type::{DeveloperDeviceType, dev_url}, - teams::DeveloperTeam, - }, - util::plist::SensitivePlistAttachment, +use crate::dev::{ + app_ids::AppId, + developer_session::DeveloperSession, + device_type::{DeveloperDeviceType, dev_url}, + teams::DeveloperTeam, }; -use plist::{Date, Dictionary, Value}; use plist_macro::plist; use rootcause::prelude::*; use serde::Deserialize; @@ -70,6 +67,30 @@ pub trait AppGroupsApi { Ok(app_group) } + + async fn assign_app_group( + &self, + team: &DeveloperTeam, + app_group: &AppGroup, + app_id: &AppId, + device_type: impl Into> + Send, + ) -> Result<(), Report> { + let body = plist!(dict { + "teamId": &team.team_id, + "applicationGroups": &app_group.application_group, + "appIdId": &app_id.app_id_id, + }); + + self.developer_session() + .send_dev_request_no_response( + &dev_url("assignApplicationGroupToAppId", device_type), + body, + ) + .await + .context("Failed to assign developer app group")?; + + Ok(()) + } } impl AppGroupsApi for DeveloperSession<'_> { diff --git a/isideload/src/dev/app_ids.rs b/isideload/src/dev/app_ids.rs index baf68a1..b9a9386 100644 --- a/isideload/src/dev/app_ids.rs +++ b/isideload/src/dev/app_ids.rs @@ -6,7 +6,7 @@ use crate::{ }, util::plist::SensitivePlistAttachment, }; -use plist::{Date, Dictionary, Value}; +use plist::{Data, Date, Dictionary, Value}; use plist_macro::plist; use rootcause::prelude::*; use serde::Deserialize; @@ -29,6 +29,27 @@ pub struct ListAppIdsResponse { pub available_quantity: Option, } +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Profile { + pub encoded_profile: Data, + pub filename: String, + pub provisioning_profile_id: String, + pub name: String, + pub status: String, + pub r#type: String, + pub distribution_method: String, + pub pro_pro_platorm: Option, + #[serde(rename = "UUID")] + pub uuid: String, + pub date_expire: Date, + pub managing_app: Option, + pub app_id_id: String, + pub is_template_profile: bool, + pub is_team_profile: Option, + pub is_free_provisioning_profile: Option, +} + #[async_trait::async_trait] pub trait AppIdsApi { fn developer_session(&self) -> &DeveloperSession<'_>; @@ -89,7 +110,7 @@ pub trait AppIdsApi { &self, team: &DeveloperTeam, app_id: &AppId, - features: &Dictionary, + features: Dictionary, device_type: impl Into> + Send, ) -> Result { let mut body = plist!(dict { @@ -126,6 +147,30 @@ pub trait AppIdsApi { Ok(()) } + + async fn download_team_provisioning_profile( + &self, + team: &DeveloperTeam, + app_id: &AppId, + device_type: impl Into> + Send, + ) -> Result { + let body = plist!(dict { + "teamId": &team.team_id, + "appIdId": &app_id.app_id_id, + }); + + let response: Profile = self + .developer_session() + .send_dev_request( + &dev_url("downloadTeamProvisioningProfile", device_type), + body, + "provisioningProfile", + ) + .await + .context("Failed to download provisioning profile")?; + + Ok(response) + } } impl AppIdsApi for DeveloperSession<'_> { diff --git a/isideload/src/dev/certificates.rs b/isideload/src/dev/certificates.rs index bc434e5..9aaaa86 100644 --- a/isideload/src/dev/certificates.rs +++ b/isideload/src/dev/certificates.rs @@ -3,10 +3,10 @@ use crate::dev::{ device_type::{DeveloperDeviceType, dev_url}, teams::DeveloperTeam, }; +use plist::Data; use plist_macro::plist; use rootcause::prelude::*; use serde::Deserialize; -use serde_bytes::ByteBuf; use uuid::Uuid; #[derive(Deserialize, Clone)] @@ -16,7 +16,7 @@ pub struct DevelopmentCertificate { pub certificate_id: Option, pub serial_number: Option, pub machine_id: Option, - pub cert_content: Option, + pub cert_content: Option, } #[derive(Deserialize, Debug, Clone)] @@ -38,7 +38,7 @@ impl std::fmt::Debug for DevelopmentCertificate { &self .cert_content .as_ref() - .map(|c| format!("Some([{} bytes])", c.len())) + .map(|c| format!("Some([{} bytes])", c.as_ref().len())) .unwrap_or("None".to_string()), ) .finish() From 0073ab2568e636b420def1b6fd4507a9687978a5 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 31 Jan 2026 00:44:08 -0500 Subject: [PATCH 34/71] In progress small refactor --- isideload/src/anisette/data.rs | 147 +++++++++++++++++++++++++ isideload/src/auth/apple_account.rs | 8 +- isideload/src/dev/developer_session.rs | 10 +- 3 files changed, 159 insertions(+), 6 deletions(-) create mode 100644 isideload/src/anisette/data.rs diff --git a/isideload/src/anisette/data.rs b/isideload/src/anisette/data.rs new file mode 100644 index 0000000..fbd6f4f --- /dev/null +++ b/isideload/src/anisette/data.rs @@ -0,0 +1,147 @@ +use crate::{ + anisette::{AnisetteProvider, AnisetteProviderConfig, remote_v3::state::AnisetteState}, + auth::grandslam::GrandSlam, +}; +use plist::Dictionary; +use plist_macro::plist; +use reqwest::header::HeaderMap; +use rootcause::prelude::*; +use serde::Deserialize; +use std::{collections::HashMap, time::SystemTime}; + +#[derive(Deserialize, Debug, Clone)] +pub struct AnisetteClientInfo { + pub client_info: String, + pub user_agent: String, +} + +#[derive(Debug, Clone)] +pub struct AnisetteData { + pub routing_info: String, + pub machine_id: String, + pub one_time_password: String, + pub device_description: String, + pub device_unique_identifier: String, + pub local_user_id: String, + pub generated_at: SystemTime, +} + +// Some headers don't seem to be required. I guess not including them is technically more efficient soooo +impl AnisetteData { + pub fn get_headers(&self) -> HashMap { + //let dt: DateTime = Utc::now().round_subsecs(0); + + HashMap::from_iter( + [ + // ( + // "X-Apple-I-Client-Time".to_string(), + // dt.format("%+").to_string().replace("+00:00", "Z"), + // ), + // ("X-Apple-I-SRL-NO".to_string(), serial), + // ("X-Apple-I-TimeZone".to_string(), "UTC".to_string()), + // ("X-Apple-Locale".to_string(), "en_US".to_string()), + // ("X-Apple-I-MD-RINFO".to_string(), self.routing_info.clone()), + // ("X-Apple-I-MD-LU".to_string(), self.local_user_id.clone()), + ( + "X-Mme-Device-Id".to_string(), + self.device_unique_identifier.clone(), + ), + ("X-Apple-I-MD".to_string(), self.one_time_password.clone()), + ("X-Apple-I-MD-M".to_string(), self.machine_id.clone()), + // ( + // "X-Mme-Client-Info".to_string(), + // self.device_description.clone(), + // ), + ] + .into_iter(), + ) + } + + pub fn get_header_map(&self) -> HeaderMap { + let headers_map = self.get_headers(); + let mut header_map = HeaderMap::new(); + + for (key, value) in headers_map { + header_map.insert( + reqwest::header::HeaderName::from_bytes(key.as_bytes()).unwrap(), + reqwest::header::HeaderValue::from_str(&value).unwrap(), + ); + } + + header_map + } + + pub fn get_client_provided_data(&self) -> Dictionary { + let headers = self.get_headers(); + + let mut cpd = plist!(dict { + "bootstrap": "true", + "icscrec": "true", + "loc": "en_US", + "pbe": "false", + "prkgen": "true", + "svct": "iCloud" + }); + + for (key, value) in headers { + cpd.insert(key.to_string(), plist::Value::String(value)); + } + + cpd + } + + pub fn needs_refresh(&self) -> bool { + let elapsed = self.generated_at.elapsed().unwrap(); + elapsed.as_secs() > 60 + } +} + +pub struct RenewableAnisetteData { + config: AnisetteProviderConfig, + anisette_data: Option, + client_info: Option, + state: Option, +} + +impl RenewableAnisetteData { + pub fn new(config: AnisetteProviderConfig) -> Self { + RenewableAnisetteData { + config, + anisette_data: None, + client_info: None, + state: None, + } + } + + pub async fn get_anisette_data(&mut self, gs: &mut GrandSlam) -> Result<&AnisetteData, Report> { + if self + .anisette_data + .as_ref() + .map_or(true, |data| data.needs_refresh()) + { + if self.client_info.is_none() || self.state.is_none() { + let mut provider = self.config.get_provider(self.client_info.clone()); + let client_info = provider.get_client_info().await?; + self.client_info = Some(client_info); + let data = provider.get_anisette_data(gs).await?; + self.anisette_data = Some(data); + } else { + } + } + + Ok(self.anisette_data.as_ref().unwrap()) + } + + pub async fn get_client_info( + &mut self, + gs: &mut GrandSlam, + ) -> Result { + self.get_anisette_data(gs).await?; + + if let Some(client_info) = &self.client_info { + return Ok(client_info.clone()); + } else { + bail!("Anisette client info not available"); + } + } +} diff --git a/isideload/src/auth/apple_account.rs b/isideload/src/auth/apple_account.rs index 4f60d9f..5dffd84 100644 --- a/isideload/src/auth/apple_account.rs +++ b/isideload/src/auth/apple_account.rs @@ -1,3 +1,5 @@ +use std::sync::{Arc, Mutex}; + use crate::{ anisette::{AnisetteData, AnisetteProvider}, auth::{ @@ -28,7 +30,7 @@ use tracing::{debug, info, warn}; pub struct AppleAccount { pub email: String, pub spd: Option, - pub anisette_provider: Box, + pub anisette_provider: Arc>, pub anisette_data: AnisetteData, pub grandslam_client: GrandSlam, login_state: LoginState, @@ -62,7 +64,7 @@ impl AppleAccount { /// - `debug`: DANGER, If true, accept invalid certificates and enable verbose connection pub async fn new( email: &str, - mut anisette_provider: Box, + mut anisette_provider: Arc>, debug: bool, ) -> Result { info!("Initializing apple account"); @@ -71,6 +73,8 @@ impl AppleAccount { } let client_info = anisette_provider + .lock() + .unwrap() .get_client_info() .await .context("Failed to get anisette client info")?; diff --git a/isideload/src/dev/developer_session.rs b/isideload/src/dev/developer_session.rs index 1ecce74..0c80303 100644 --- a/isideload/src/dev/developer_session.rs +++ b/isideload/src/dev/developer_session.rs @@ -1,3 +1,5 @@ +use std::sync::{Arc, Mutex}; + use plist::Dictionary; use plist_macro::{plist, plist_to_xml_string}; use rootcause::prelude::*; @@ -6,7 +8,7 @@ use tracing::{error, warn}; use uuid::Uuid; use crate::{ - anisette::AnisetteData, + anisette::{AnisetteData, AnisetteProvider}, auth::{ apple_account::{AppToken, AppleAccount}, grandslam::GrandSlam, @@ -25,7 +27,7 @@ pub struct DeveloperSession<'a> { token: AppToken, adsid: String, client: &'a GrandSlam, - anisette_data: &'a AnisetteData, + anisette_provider: Arc>, } impl<'a> DeveloperSession<'a> { @@ -33,13 +35,13 @@ impl<'a> DeveloperSession<'a> { token: AppToken, adsid: String, client: &'a GrandSlam, - anisette_data: &'a AnisetteData, + anisette_provider: Arc>, ) -> Self { DeveloperSession { token, adsid, client, - anisette_data, + anisette_provider, } } From 41e29caea865842f9039bc82793ce38120145e86 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 1 Feb 2026 01:01:54 -0500 Subject: [PATCH 35/71] Add refreshing anisette & Rethink ownership for GrandSlam and anisette to prepare for concurrency --- Cargo.lock | 1 + examples/minimal/src/main.rs | 2 +- isideload/Cargo.toml | 1 + isideload/src/anisette/data.rs | 147 ------------------------ isideload/src/anisette/mod.rs | 67 ++++++++++- isideload/src/anisette/remote_v3/mod.rs | 42 +++++-- isideload/src/auth/apple_account.rs | 80 +++++++------ isideload/src/auth/builder.rs | 26 +++-- isideload/src/auth/grandslam.rs | 92 ++++++++------- isideload/src/dev/app_groups.rs | 12 +- isideload/src/dev/app_ids.rs | 16 +-- isideload/src/dev/certificates.rs | 12 +- isideload/src/dev/developer_session.rs | 37 +++--- isideload/src/dev/devices.rs | 10 +- isideload/src/dev/teams.rs | 8 +- isideload/src/lib.rs | 3 + 16 files changed, 271 insertions(+), 285 deletions(-) delete mode 100644 isideload/src/anisette/data.rs diff --git a/Cargo.lock b/Cargo.lock index b72407b..d23a994 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -824,6 +824,7 @@ dependencies = [ "serde_json", "sha2", "thiserror 2.0.18", + "tokio", "tokio-tungstenite", "tracing", "uuid", diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index a68aeb3..03f1caa 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -49,7 +49,7 @@ async fn main() { let mut account = account.unwrap(); - let dev_session = DeveloperSession::from_account(&mut account) + let mut dev_session = DeveloperSession::from_account(&mut account) .await .expect("Failed to create developer session"); diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 657d926..45726e5 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -38,3 +38,4 @@ hmac = "0.12.1" cbc = { version = "0.1.2", features = ["std"] } aes = "0.8.4" aes-gcm = "0.10.3" +tokio = "1.49.0" diff --git a/isideload/src/anisette/data.rs b/isideload/src/anisette/data.rs deleted file mode 100644 index fbd6f4f..0000000 --- a/isideload/src/anisette/data.rs +++ /dev/null @@ -1,147 +0,0 @@ -use crate::{ - anisette::{AnisetteProvider, AnisetteProviderConfig, remote_v3::state::AnisetteState}, - auth::grandslam::GrandSlam, -}; -use plist::Dictionary; -use plist_macro::plist; -use reqwest::header::HeaderMap; -use rootcause::prelude::*; -use serde::Deserialize; -use std::{collections::HashMap, time::SystemTime}; - -#[derive(Deserialize, Debug, Clone)] -pub struct AnisetteClientInfo { - pub client_info: String, - pub user_agent: String, -} - -#[derive(Debug, Clone)] -pub struct AnisetteData { - pub routing_info: String, - pub machine_id: String, - pub one_time_password: String, - pub device_description: String, - pub device_unique_identifier: String, - pub local_user_id: String, - pub generated_at: SystemTime, -} - -// Some headers don't seem to be required. I guess not including them is technically more efficient soooo -impl AnisetteData { - pub fn get_headers(&self) -> HashMap { - //let dt: DateTime = Utc::now().round_subsecs(0); - - HashMap::from_iter( - [ - // ( - // "X-Apple-I-Client-Time".to_string(), - // dt.format("%+").to_string().replace("+00:00", "Z"), - // ), - // ("X-Apple-I-SRL-NO".to_string(), serial), - // ("X-Apple-I-TimeZone".to_string(), "UTC".to_string()), - // ("X-Apple-Locale".to_string(), "en_US".to_string()), - // ("X-Apple-I-MD-RINFO".to_string(), self.routing_info.clone()), - // ("X-Apple-I-MD-LU".to_string(), self.local_user_id.clone()), - ( - "X-Mme-Device-Id".to_string(), - self.device_unique_identifier.clone(), - ), - ("X-Apple-I-MD".to_string(), self.one_time_password.clone()), - ("X-Apple-I-MD-M".to_string(), self.machine_id.clone()), - // ( - // "X-Mme-Client-Info".to_string(), - // self.device_description.clone(), - // ), - ] - .into_iter(), - ) - } - - pub fn get_header_map(&self) -> HeaderMap { - let headers_map = self.get_headers(); - let mut header_map = HeaderMap::new(); - - for (key, value) in headers_map { - header_map.insert( - reqwest::header::HeaderName::from_bytes(key.as_bytes()).unwrap(), - reqwest::header::HeaderValue::from_str(&value).unwrap(), - ); - } - - header_map - } - - pub fn get_client_provided_data(&self) -> Dictionary { - let headers = self.get_headers(); - - let mut cpd = plist!(dict { - "bootstrap": "true", - "icscrec": "true", - "loc": "en_US", - "pbe": "false", - "prkgen": "true", - "svct": "iCloud" - }); - - for (key, value) in headers { - cpd.insert(key.to_string(), plist::Value::String(value)); - } - - cpd - } - - pub fn needs_refresh(&self) -> bool { - let elapsed = self.generated_at.elapsed().unwrap(); - elapsed.as_secs() > 60 - } -} - -pub struct RenewableAnisetteData { - config: AnisetteProviderConfig, - anisette_data: Option, - client_info: Option, - state: Option, -} - -impl RenewableAnisetteData { - pub fn new(config: AnisetteProviderConfig) -> Self { - RenewableAnisetteData { - config, - anisette_data: None, - client_info: None, - state: None, - } - } - - pub async fn get_anisette_data(&mut self, gs: &mut GrandSlam) -> Result<&AnisetteData, Report> { - if self - .anisette_data - .as_ref() - .map_or(true, |data| data.needs_refresh()) - { - if self.client_info.is_none() || self.state.is_none() { - let mut provider = self.config.get_provider(self.client_info.clone()); - let client_info = provider.get_client_info().await?; - self.client_info = Some(client_info); - let data = provider.get_anisette_data(gs).await?; - self.anisette_data = Some(data); - } else { - } - } - - Ok(self.anisette_data.as_ref().unwrap()) - } - - pub async fn get_client_info( - &mut self, - gs: &mut GrandSlam, - ) -> Result { - self.get_anisette_data(gs).await?; - - if let Some(client_info) = &self.client_info { - return Ok(client_info.clone()); - } else { - bail!("Anisette client info not available"); - } - } -} diff --git a/isideload/src/anisette/mod.rs b/isideload/src/anisette/mod.rs index 4514634..29c4bd0 100644 --- a/isideload/src/anisette/mod.rs +++ b/isideload/src/anisette/mod.rs @@ -6,7 +6,8 @@ use plist_macro::plist; use reqwest::header::HeaderMap; use rootcause::prelude::*; use serde::Deserialize; -use std::collections::HashMap; +use std::{collections::HashMap, sync::Arc, time::SystemTime}; +use tokio::sync::RwLock; #[derive(Deserialize, Debug, Clone)] pub struct AnisetteClientInfo { @@ -22,6 +23,7 @@ pub struct AnisetteData { _device_description: String, device_unique_identifier: String, _local_user_id: String, + generated_at: SystemTime, } // Some headers don't seem to be required. I guess not including them is technically more efficient soooo @@ -87,11 +89,72 @@ impl AnisetteData { cpd } + + pub fn needs_refresh(&self) -> bool { + let elapsed = self.generated_at.elapsed().unwrap(); + elapsed.as_secs() > 60 + } } #[async_trait::async_trait] pub trait AnisetteProvider { - async fn get_anisette_data(&mut self, gs: &mut GrandSlam) -> Result; + async fn get_anisette_data(&self) -> Result; async fn get_client_info(&mut self) -> Result; + + async fn provision(&mut self, gs: Arc) -> Result<(), Report>; + + fn needs_provisioning(&self) -> Result; +} + +#[derive(Clone)] +pub struct AnisetteDataGenerator { + provider: Arc>, + data: Option>, +} + +impl AnisetteDataGenerator { + pub fn new(provider: Arc>) -> Self { + AnisetteDataGenerator { + provider, + data: None, + } + } + + pub async fn get_anisette_data( + &mut self, + gs: Arc, + ) -> Result, Report> { + if let Some(data) = &self.data { + if !data.needs_refresh() { + return Ok(data.clone()); + } + } + + // trying to avoid locking as write unless necessary to promote concurrency + let provider = self.provider.read().await; + + if provider.needs_provisioning()? { + drop(provider); + let mut provider_write = self.provider.write().await; + provider_write.provision(gs).await?; + drop(provider_write); + + let provider = self.provider.read().await; + let data = provider.get_anisette_data().await?; + let arc_data = Arc::new(data); + self.data = Some(arc_data.clone()); + Ok(arc_data) + } else { + let data = provider.get_anisette_data().await?; + let arc_data = Arc::new(data); + self.data = Some(arc_data.clone()); + Ok(arc_data) + } + } + + pub async fn get_client_info(&self) -> Result { + let mut provider = self.provider.write().await; + provider.get_client_info().await + } } diff --git a/isideload/src/anisette/remote_v3/mod.rs b/isideload/src/anisette/remote_v3/mod.rs index 8a39140..cee12a5 100644 --- a/isideload/src/anisette/remote_v3/mod.rs +++ b/isideload/src/anisette/remote_v3/mod.rs @@ -2,6 +2,8 @@ mod state; use std::fs; use std::path::PathBuf; +use std::sync::Arc; +use std::time::SystemTime; use base64::prelude::*; use plist_macro::plist; @@ -11,6 +13,7 @@ use serde::Deserialize; use tokio_tungstenite::tungstenite::Message; use tracing::{debug, info}; +use crate::SideloadError; use crate::anisette::remote_v3::state::AnisetteState; use crate::anisette::{AnisetteClientInfo, AnisetteData, AnisetteProvider}; use crate::auth::grandslam::GrandSlam; @@ -74,13 +77,19 @@ impl Default for RemoteV3AnisetteProvider { #[async_trait::async_trait] impl AnisetteProvider for RemoteV3AnisetteProvider { - async fn get_anisette_data(&mut self, gs: &mut GrandSlam) -> Result { - let state = self.get_state(gs).await?.clone(); + async fn get_anisette_data(&self) -> Result { + let state = self + .state + .as_ref() + .ok_or(SideloadError::AnisetteNotProvisioned)?; let adi_pb = state .adi_pb .as_ref() - .ok_or(report!("Anisette state is not provisioned"))?; - let client_info = self.get_client_info().await?.client_info.clone(); + .ok_or(SideloadError::AnisetteNotProvisioned)?; + let client_info = self + .client_info + .as_ref() + .ok_or(SideloadError::AnisetteNotProvisioned)?; let headers = self .client @@ -109,9 +118,10 @@ impl AnisetteProvider for RemoteV3AnisetteProvider { machine_id, one_time_password, routing_info, - _device_description: client_info, + _device_description: client_info.client_info.clone(), device_unique_identifier: state.get_device_id(), _local_user_id: hex::encode(&state.get_md_lu()), + generated_at: SystemTime::now(), }; Ok(data) @@ -140,10 +150,24 @@ impl AnisetteProvider for RemoteV3AnisetteProvider { Ok(self.client_info.as_ref().unwrap().clone()) } + + fn needs_provisioning(&self) -> Result { + if let Some(state) = &self.state { + Ok(!state.is_provisioned() || self.client_info.is_none()) + } else { + Ok(true) + } + } + + async fn provision(&mut self, gs: Arc) -> Result<(), Report> { + self.get_client_info().await?; + self.get_state(gs).await?; + Ok(()) + } } impl RemoteV3AnisetteProvider { - async fn get_state(&mut self, gs: &mut GrandSlam) -> Result<&mut AnisetteState, Report> { + async fn get_state(&mut self, gs: Arc) -> Result<&mut AnisetteState, Report> { let state_path = self.config_path.join("state.plist"); fs::create_dir_all(&self.config_path)?; if self.state.is_none() { @@ -195,13 +219,13 @@ impl RemoteV3AnisetteProvider { } async fn provision( state: &mut AnisetteState, - gs: &mut GrandSlam, + gs: Arc, url: &str, ) -> Result<(), Report> { debug!("Starting provisioning"); - let start_provisioning = gs.get_url("midStartProvisioning").await?; - let end_provisioning = gs.get_url("midFinishProvisioning").await?; + let start_provisioning = gs.get_url("midStartProvisioning")?; + let end_provisioning = gs.get_url("midFinishProvisioning")?; let websocket_url = format!("{}/v3/provisioning_session", url) .replace("https://", "wss://") diff --git a/isideload/src/auth/apple_account.rs b/isideload/src/auth/apple_account.rs index 5dffd84..d665543 100644 --- a/isideload/src/auth/apple_account.rs +++ b/isideload/src/auth/apple_account.rs @@ -1,7 +1,7 @@ -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use crate::{ - anisette::{AnisetteData, AnisetteProvider}, + anisette::{AnisetteData, AnisetteDataGenerator}, auth::{ builder::AppleAccountBuilder, grandslam::{GrandSlam, GrandSlamErrorChecker}, @@ -30,9 +30,8 @@ use tracing::{debug, info, warn}; pub struct AppleAccount { pub email: String, pub spd: Option, - pub anisette_provider: Arc>, - pub anisette_data: AnisetteData, - pub grandslam_client: GrandSlam, + pub anisette_generator: AnisetteDataGenerator, + pub grandslam_client: Arc, login_state: LoginState, debug: bool, } @@ -64,7 +63,7 @@ impl AppleAccount { /// - `debug`: DANGER, If true, accept invalid certificates and enable verbose connection pub async fn new( email: &str, - mut anisette_provider: Arc>, + anisette_generator: AnisetteDataGenerator, debug: bool, ) -> Result { info!("Initializing apple account"); @@ -72,26 +71,18 @@ impl AppleAccount { warn!("Debug mode enabled: this is a security risk!"); } - let client_info = anisette_provider - .lock() - .unwrap() + let client_info = anisette_generator .get_client_info() .await .context("Failed to get anisette client info")?; - let mut grandslam_client = GrandSlam::new(client_info, debug); - - let anisette_data = anisette_provider - .get_anisette_data(&mut grandslam_client) - .await - .context("Failed to get anisette data for login")?; + let grandslam_client = GrandSlam::new(client_info, debug).await?; Ok(AppleAccount { email: email.to_string(), spd: None, - anisette_provider, - anisette_data, - grandslam_client, + anisette_generator, + grandslam_client: Arc::new(grandslam_client), debug, login_state: LoginState::NeedsLogin, }) @@ -197,16 +188,22 @@ impl AppleAccount { two_factor_callback: impl Fn() -> Option, ) -> Result<(), Report> { debug!("Trusted device 2FA required"); + + let anisette_data = self + .anisette_generator + .get_anisette_data(self.grandslam_client.clone()) + .await + .context("Failed to get anisette data for 2FA")?; + let request_code_url = self .grandslam_client - .get_url("trustedDeviceSecondaryAuth") - .await?; + .get_url("trustedDeviceSecondaryAuth")?; - let submit_code_url = self.grandslam_client.get_url("validateCode").await?; + let submit_code_url = self.grandslam_client.get_url("validateCode")?; self.grandslam_client .get(&request_code_url)? - .headers(self.build_2fa_headers().await?) + .headers(self.build_2fa_headers(&anisette_data).await?) .send() .await .context("Failed to request trusted device 2fa")? @@ -221,7 +218,7 @@ impl AppleAccount { let res = self .grandslam_client .get(&submit_code_url)? - .headers(self.build_2fa_headers().await?) + .headers(self.build_2fa_headers(&anisette_data).await?) .header("security-code", code) .send() .await @@ -248,11 +245,17 @@ impl AppleAccount { ) -> Result<(), Report> { debug!("SMS 2FA required"); - let request_code_url = self.grandslam_client.get_url("secondaryAuth").await?; + let anisette_data = self + .anisette_generator + .get_anisette_data(self.grandslam_client.clone()) + .await + .context("Failed to get anisette data for 2FA")?; + + let request_code_url = self.grandslam_client.get_url("secondaryAuth")?; self.grandslam_client .get_sms(&request_code_url)? - .headers(self.build_2fa_headers().await?) + .headers(self.build_2fa_headers(&anisette_data).await?) .send() .await .context("Failed to request SMS 2FA")? @@ -274,7 +277,7 @@ impl AppleAccount { "mode": "sms" }); - let mut headers = self.build_2fa_headers().await?; + let mut headers = self.build_2fa_headers(&anisette_data).await?; headers.insert("Content-Type", HeaderValue::from_static("application/json")); headers.insert( "Accept", @@ -332,8 +335,8 @@ impl AppleAccount { Ok(()) } - async fn build_2fa_headers(&mut self) -> Result { - let mut headers = self.anisette_data.get_header_map(); + async fn build_2fa_headers(&self, anisette_data: &AnisetteData) -> Result { + let mut headers = anisette_data.get_header_map(); let spd = self .spd @@ -354,18 +357,23 @@ impl AppleAccount { ); headers.insert( "X-Apple-I-MD-RINFO", - reqwest::header::HeaderValue::from_str(&self.anisette_data.routing_info)?, + reqwest::header::HeaderValue::from_str(&anisette_data.routing_info)?, ); Ok(headers) } async fn login_inner(&mut self, password: &str) -> Result { - let gs_service_url = self.grandslam_client.get_url("gsService").await?; + let anisette_data = self + .anisette_generator + .get_anisette_data(self.grandslam_client.clone()) + .await + .context("Failed to get anisette data for login")?; + let gs_service_url = self.grandslam_client.get_url("gsService")?; debug!("GrandSlam service URL: {}", gs_service_url); - let cpd = self.anisette_data.get_client_provided_data(); + let cpd = anisette_data.get_client_provided_data(); let srp_client = SrpClient::::new(&G_2048); let a: Vec = (0..32).map(|_| rand::random::()).collect(); @@ -510,6 +518,12 @@ impl AppleAccount { format!("com.apple.gs.{}", app) }; + let anisette_data = self + .anisette_generator + .get_anisette_data(self.grandslam_client.clone()) + .await + .context("Failed to get anisette data for login")?; + let spd = self .spd .as_ref() @@ -531,8 +545,8 @@ impl AppleAccount { .into_bytes() .to_vec(); - let gs_service_url = self.grandslam_client.get_url("gsService").await?; - let cpd = self.anisette_data.get_client_provided_data(); + let gs_service_url = self.grandslam_client.get_url("gsService")?; + let cpd = anisette_data.get_client_provided_data(); let request = plist!(dict { "Header": { diff --git a/isideload/src/auth/builder.rs b/isideload/src/auth/builder.rs index 18bd03e..54fa29e 100644 --- a/isideload/src/auth/builder.rs +++ b/isideload/src/auth/builder.rs @@ -1,14 +1,17 @@ +use std::sync::Arc; + use rootcause::prelude::*; +use tokio::sync::RwLock; use crate::{ - anisette::{AnisetteProvider, remote_v3::RemoteV3AnisetteProvider}, + anisette::{AnisetteDataGenerator, AnisetteProvider, remote_v3::RemoteV3AnisetteProvider}, auth::apple_account::AppleAccount, }; pub struct AppleAccountBuilder { email: String, debug: Option, - anisette_provider: Option>, + anisette_generator: Option, } impl AppleAccountBuilder { @@ -20,7 +23,7 @@ impl AppleAccountBuilder { Self { email: email.to_string(), debug: None, - anisette_provider: None, + anisette_generator: None, } } @@ -33,8 +36,13 @@ impl AppleAccountBuilder { self } - pub fn anisette_provider(mut self, anisette_provider: impl AnisetteProvider + 'static) -> Self { - self.anisette_provider = Some(Box::new(anisette_provider)); + pub fn anisette_provider( + mut self, + anisette_provider: impl AnisetteProvider + Send + Sync + 'static, + ) -> Self { + self.anisette_generator = Some(AnisetteDataGenerator::new(Arc::new(RwLock::new( + anisette_provider, + )))); self } @@ -44,11 +52,11 @@ impl AppleAccountBuilder { /// Returns an error if the reqwest client cannot be built pub async fn build(self) -> Result { let debug = self.debug.unwrap_or(false); - let anisette_provider = self - .anisette_provider - .unwrap_or_else(|| Box::new(RemoteV3AnisetteProvider::default())); + let anisette_generator = self.anisette_generator.unwrap_or_else(|| { + AnisetteDataGenerator::new(Arc::new(RwLock::new(RemoteV3AnisetteProvider::default()))) + }); - AppleAccount::new(&self.email, anisette_provider, debug).await + AppleAccount::new(&self.email, anisette_generator, debug).await } /// Build the AppleAccount and log in diff --git a/isideload/src/auth/grandslam.rs b/isideload/src/auth/grandslam.rs index 44133fa..150adbb 100644 --- a/isideload/src/auth/grandslam.rs +++ b/isideload/src/auth/grandslam.rs @@ -16,7 +16,7 @@ const URL_BAG: &str = "https://gsa.apple.com/grandslam/GsService2/lookup"; pub struct GrandSlam { pub client: reqwest::Client, pub client_info: AnisetteClientInfo, - pub url_bag: Option, + url_bag: Dictionary, } impl GrandSlam { @@ -24,64 +24,75 @@ impl GrandSlam { /// /// # Arguments /// - `client`: The reqwest client to use for requests - pub fn new(client_info: AnisetteClientInfo, debug: bool) -> Self { - Self { - client: Self::build_reqwest_client(debug).unwrap(), + pub async fn new(client_info: AnisetteClientInfo, debug: bool) -> Result { + let client = Self::build_reqwest_client(debug).unwrap(); + let base_headers = Self::base_headers(&client_info, false)?; + let url_bag = Self::fetch_url_bag(&client, base_headers).await?; + Ok(Self { + client, client_info, - url_bag: None, - } + url_bag, + }) } - /// Get the URL bag from GrandSlam - pub async fn get_url_bag(&mut self) -> Result<&Dictionary, Report> { - if self.url_bag.is_none() { - debug!("Fetching URL bag from GrandSlam"); - let resp = self - .client - .get(URL_BAG) - .headers(self.base_headers(false)?) - .send() - .await - .context("Failed to fetch URL Bag")? - .text() - .await - .context("Failed to read URL Bag response text")?; + /// Fetch the URL bag from GrandSlam and cache it + pub async fn fetch_url_bag( + client: &reqwest::Client, + base_headers: HeaderMap, + ) -> Result { + debug!("Fetching URL bag from GrandSlam"); + let resp = client + .get(URL_BAG) + .headers(base_headers) + .send() + .await + .context("Failed to fetch URL Bag")? + .text() + .await + .context("Failed to read URL Bag response text")?; - let dict: Dictionary = - plist::from_bytes(resp.as_bytes()).context("Failed to parse URL Bag plist")?; - let urls = dict - .get("urls") - .and_then(|v| v.as_dictionary()) - .cloned() - .ok_or_else(|| report!("URL Bag plist missing 'urls' dictionary"))?; + let dict: Dictionary = + plist::from_bytes(resp.as_bytes()).context("Failed to parse URL Bag plist")?; + let urls = dict + .get("urls") + .and_then(|v| v.as_dictionary()) + .cloned() + .ok_or_else(|| report!("URL Bag plist missing 'urls' dictionary"))?; - self.url_bag = Some(urls); - } - Ok(self.url_bag.as_ref().unwrap()) + Ok(urls) } - pub async fn get_url(&mut self, key: &str) -> Result { - let url_bag = self.get_url_bag().await?; - let url = url_bag + pub fn get_url(&self, key: &str) -> Result { + let url = self + .url_bag .get_string(key) .context("Unable to find key in URL bag")?; Ok(url) } pub fn get(&self, url: &str) -> Result { - let builder = self.client.get(url).headers(self.base_headers(false)?); + let builder = self + .client + .get(url) + .headers(Self::base_headers(&self.client_info, false)?); Ok(builder) } pub fn get_sms(&self, url: &str) -> Result { - let builder = self.client.get(url).headers(self.base_headers(true)?); + let builder = self + .client + .get(url) + .headers(Self::base_headers(&self.client_info, true)?); Ok(builder) } pub fn post(&self, url: &str) -> Result { - let builder = self.client.post(url).headers(self.base_headers(false)?); + let builder = self + .client + .post(url) + .headers(Self::base_headers(&self.client_info, false)?); Ok(builder) } @@ -121,7 +132,10 @@ impl GrandSlam { Ok(response_plist) } - fn base_headers(&self, sms: bool) -> Result { + fn base_headers( + client_info: &AnisetteClientInfo, + sms: bool, + ) -> Result { let mut headers = reqwest::header::HeaderMap::new(); if !sms { headers.insert("Content-Type", HeaderValue::from_static("text/x-xml-plist")); @@ -129,11 +143,11 @@ impl GrandSlam { } headers.insert( "X-Mme-Client-Info", - HeaderValue::from_str(&self.client_info.client_info)?, + HeaderValue::from_str(&client_info.client_info)?, ); headers.insert( "User-Agent", - HeaderValue::from_str(&self.client_info.user_agent)?, + HeaderValue::from_str(&client_info.user_agent)?, ); headers.insert("X-Xcode-Version", HeaderValue::from_static("14.2 (14C18)")); headers.insert( diff --git a/isideload/src/dev/app_groups.rs b/isideload/src/dev/app_groups.rs index 2d0f5f9..881153b 100644 --- a/isideload/src/dev/app_groups.rs +++ b/isideload/src/dev/app_groups.rs @@ -18,10 +18,10 @@ pub struct AppGroup { #[async_trait::async_trait] pub trait AppGroupsApi { - fn developer_session(&self) -> &DeveloperSession<'_>; + fn developer_session(&mut self) -> &mut DeveloperSession; async fn list_app_groups( - &self, + &mut self, team: &DeveloperTeam, device_type: impl Into> + Send, ) -> Result, Report> { @@ -43,7 +43,7 @@ pub trait AppGroupsApi { } async fn add_app_group( - &self, + &mut self, team: &DeveloperTeam, name: &str, identifier: &str, @@ -69,7 +69,7 @@ pub trait AppGroupsApi { } async fn assign_app_group( - &self, + &mut self, team: &DeveloperTeam, app_group: &AppGroup, app_id: &AppId, @@ -93,8 +93,8 @@ pub trait AppGroupsApi { } } -impl AppGroupsApi for DeveloperSession<'_> { - fn developer_session(&self) -> &DeveloperSession<'_> { +impl AppGroupsApi for DeveloperSession { + fn developer_session(&mut self) -> &mut DeveloperSession { self } } diff --git a/isideload/src/dev/app_ids.rs b/isideload/src/dev/app_ids.rs index b9a9386..c546438 100644 --- a/isideload/src/dev/app_ids.rs +++ b/isideload/src/dev/app_ids.rs @@ -52,10 +52,10 @@ pub struct Profile { #[async_trait::async_trait] pub trait AppIdsApi { - fn developer_session(&self) -> &DeveloperSession<'_>; + fn developer_session(&mut self) -> &mut DeveloperSession; async fn add_app_id( - &self, + &mut self, team: &DeveloperTeam, name: &str, identifier: &str, @@ -77,7 +77,7 @@ pub trait AppIdsApi { } async fn list_app_ids( - &self, + &mut self, team: &DeveloperTeam, device_type: impl Into> + Send, ) -> Result { @@ -107,7 +107,7 @@ pub trait AppIdsApi { } async fn update_app_id( - &self, + &mut self, team: &DeveloperTeam, app_id: &AppId, features: Dictionary, @@ -130,7 +130,7 @@ pub trait AppIdsApi { } async fn delete_app_id( - &self, + &mut self, team: &DeveloperTeam, app_id: &AppId, device_type: impl Into> + Send, @@ -149,7 +149,7 @@ pub trait AppIdsApi { } async fn download_team_provisioning_profile( - &self, + &mut self, team: &DeveloperTeam, app_id: &AppId, device_type: impl Into> + Send, @@ -173,8 +173,8 @@ pub trait AppIdsApi { } } -impl AppIdsApi for DeveloperSession<'_> { - fn developer_session(&self) -> &DeveloperSession<'_> { +impl AppIdsApi for DeveloperSession { + fn developer_session(&mut self) -> &mut DeveloperSession { self } } diff --git a/isideload/src/dev/certificates.rs b/isideload/src/dev/certificates.rs index 9aaaa86..298da91 100644 --- a/isideload/src/dev/certificates.rs +++ b/isideload/src/dev/certificates.rs @@ -47,10 +47,10 @@ impl std::fmt::Debug for DevelopmentCertificate { #[async_trait::async_trait] pub trait CertificatesApi { - fn developer_session(&self) -> &DeveloperSession<'_>; + fn developer_session(&mut self) -> &mut DeveloperSession; async fn list_all_development_certs( - &self, + &mut self, team: &DeveloperTeam, device_type: impl Into> + Send, ) -> Result, Report> { @@ -72,7 +72,7 @@ pub trait CertificatesApi { } async fn revoke_development_cert( - &self, + &mut self, team: &DeveloperTeam, serial_number: &str, device_type: impl Into> + Send, @@ -94,7 +94,7 @@ pub trait CertificatesApi { } async fn submit_development_csr( - &self, + &mut self, team: &DeveloperTeam, csr_content: String, machine_name: String, @@ -121,8 +121,8 @@ pub trait CertificatesApi { } } -impl CertificatesApi for DeveloperSession<'_> { - fn developer_session(&self) -> &DeveloperSession<'_> { +impl CertificatesApi for DeveloperSession { + fn developer_session(&mut self) -> &mut DeveloperSession { self } } diff --git a/isideload/src/dev/developer_session.rs b/isideload/src/dev/developer_session.rs index 0c80303..2842719 100644 --- a/isideload/src/dev/developer_session.rs +++ b/isideload/src/dev/developer_session.rs @@ -1,4 +1,4 @@ -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use plist::Dictionary; use plist_macro::{plist, plist_to_xml_string}; @@ -8,7 +8,7 @@ use tracing::{error, warn}; use uuid::Uuid; use crate::{ - anisette::{AnisetteData, AnisetteProvider}, + anisette::AnisetteDataGenerator, auth::{ apple_account::{AppToken, AppleAccount}, grandslam::GrandSlam, @@ -23,29 +23,29 @@ pub use super::device_type::DeveloperDeviceType; pub use super::devices::*; pub use super::teams::*; -pub struct DeveloperSession<'a> { +pub struct DeveloperSession { token: AppToken, adsid: String, - client: &'a GrandSlam, - anisette_provider: Arc>, + client: Arc, + anisette_generator: AnisetteDataGenerator, } -impl<'a> DeveloperSession<'a> { +impl DeveloperSession { pub fn new( token: AppToken, adsid: String, - client: &'a GrandSlam, - anisette_provider: Arc>, + client: Arc, + anisette_generator: AnisetteDataGenerator, ) -> Self { DeveloperSession { token, adsid, client, - anisette_provider, + anisette_generator, } } - pub async fn from_account(account: &'a mut AppleAccount) -> Result { + pub async fn from_account(account: &mut AppleAccount) -> Result { let token = account .get_app_token("xcode.auth") .await @@ -59,13 +59,13 @@ impl<'a> DeveloperSession<'a> { Ok(DeveloperSession::new( token, spd.get_string("adsid")?, - &account.grandslam_client, - &account.anisette_data, + account.grandslam_client.clone(), + account.anisette_generator.clone(), )) } async fn send_dev_request_internal( - &self, + &mut self, url: &str, body: impl Into>, ) -> Result<(Dictionary, Option), Report> { @@ -86,7 +86,12 @@ impl<'a> DeveloperSession<'a> { .body(plist_to_xml_string(&body)) .header("X-Apple-GS-Token", &self.token.token) .header("X-Apple-I-Identity-Id", &self.adsid) - .headers(self.anisette_data.get_header_map()) + .headers( + self.anisette_generator + .get_anisette_data(self.client.clone()) + .await? + .get_header_map(), + ) .send() .await? .error_for_status() @@ -133,7 +138,7 @@ impl<'a> DeveloperSession<'a> { } pub async fn send_dev_request( - &self, + &mut self, url: &str, body: impl Into>, response_key: &str, @@ -152,7 +157,7 @@ impl<'a> DeveloperSession<'a> { } pub async fn send_dev_request_no_response( - &self, + &mut self, url: &str, body: impl Into>, ) -> Result { diff --git a/isideload/src/dev/devices.rs b/isideload/src/dev/devices.rs index 860f371..6c57559 100644 --- a/isideload/src/dev/devices.rs +++ b/isideload/src/dev/devices.rs @@ -18,10 +18,10 @@ pub struct DeveloperDevice { #[async_trait::async_trait] pub trait DevicesApi { - fn developer_session(&self) -> &DeveloperSession<'_>; + fn developer_session(&mut self) -> &mut DeveloperSession; async fn list_devices( - &self, + &mut self, team: &DeveloperTeam, device_type: impl Into> + Send, ) -> Result, Report> { @@ -39,7 +39,7 @@ pub trait DevicesApi { } async fn add_device( - &self, + &mut self, team: &DeveloperTeam, name: &str, udid: &str, @@ -61,8 +61,8 @@ pub trait DevicesApi { } } -impl DevicesApi for DeveloperSession<'_> { - fn developer_session(&self) -> &DeveloperSession<'_> { +impl DevicesApi for DeveloperSession { + fn developer_session(&mut self) -> &mut DeveloperSession { self } } diff --git a/isideload/src/dev/teams.rs b/isideload/src/dev/teams.rs index 5bf4b98..1a4cb66 100644 --- a/isideload/src/dev/teams.rs +++ b/isideload/src/dev/teams.rs @@ -16,9 +16,9 @@ pub struct DeveloperTeam { #[async_trait::async_trait] pub trait TeamsApi { - fn developer_session(&self) -> &DeveloperSession<'_>; + fn developer_session(&mut self) -> &mut DeveloperSession; - async fn list_teams(&self) -> Result, Report> { + async fn list_teams(&mut self) -> Result, Report> { let response: Vec = self .developer_session() .send_dev_request(&dev_url("listTeams", Any), None, "teams") @@ -29,8 +29,8 @@ pub trait TeamsApi { } } -impl TeamsApi for DeveloperSession<'_> { - fn developer_session(&self) -> &DeveloperSession<'_> { +impl TeamsApi for DeveloperSession { + fn developer_session(&mut self) -> &mut DeveloperSession { self } } diff --git a/isideload/src/lib.rs b/isideload/src/lib.rs index 9fc2232..14a1c66 100644 --- a/isideload/src/lib.rs +++ b/isideload/src/lib.rs @@ -15,6 +15,9 @@ pub enum SideloadError { #[error("Plist parse error: {0}")] PlistParseError(String), + + #[error("Failed to get anisette data, anisette not provisioned")] + AnisetteNotProvisioned, } struct ReqwestErrorFormatter; From a944dc79c7449d57ff39f5f0f0afd87fb984c102 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 1 Feb 2026 01:03:27 -0500 Subject: [PATCH 36/71] fix clippy --- isideload/src/anisette/mod.rs | 8 +++----- isideload/src/anisette/remote_v3/mod.rs | 15 ++++++--------- isideload/src/anisette/remote_v3/state.rs | 4 ++-- isideload/src/auth/apple_account.rs | 20 +++++++++----------- isideload/src/auth/grandslam.rs | 2 +- isideload/src/dev/developer_session.rs | 7 +++---- 6 files changed, 24 insertions(+), 32 deletions(-) diff --git a/isideload/src/anisette/mod.rs b/isideload/src/anisette/mod.rs index 29c4bd0..9e8c80b 100644 --- a/isideload/src/anisette/mod.rs +++ b/isideload/src/anisette/mod.rs @@ -52,8 +52,7 @@ impl AnisetteData { // "X-Mme-Client-Info".to_string(), // self.device_description.clone(), // ), - ] - .into_iter(), + ], ) } @@ -125,11 +124,10 @@ impl AnisetteDataGenerator { &mut self, gs: Arc, ) -> Result, Report> { - if let Some(data) = &self.data { - if !data.needs_refresh() { + if let Some(data) = &self.data + && !data.needs_refresh() { return Ok(data.clone()); } - } // trying to avoid locking as write unless necessary to promote concurrency let provider = self.provider.read().await; diff --git a/isideload/src/anisette/remote_v3/mod.rs b/isideload/src/anisette/remote_v3/mod.rs index cee12a5..1677544 100644 --- a/isideload/src/anisette/remote_v3/mod.rs +++ b/isideload/src/anisette/remote_v3/mod.rs @@ -97,7 +97,7 @@ impl AnisetteProvider for RemoteV3AnisetteProvider { .header(CONTENT_TYPE, "application/json") .body( serde_json::json!({ - "identifier": BASE64_STANDARD.encode(&state.keychain_identifier), + "identifier": BASE64_STANDARD.encode(state.keychain_identifier), "adi_pb": BASE64_STANDARD.encode(adi_pb) }) .to_string(), @@ -120,7 +120,7 @@ impl AnisetteProvider for RemoteV3AnisetteProvider { routing_info, _device_description: client_info.client_info.clone(), device_unique_identifier: state.get_device_id(), - _local_user_id: hex::encode(&state.get_md_lu()), + _local_user_id: hex::encode(state.get_md_lu()), generated_at: SystemTime::now(), }; @@ -128,8 +128,7 @@ impl AnisetteProvider for RemoteV3AnisetteProvider { } AnisetteHeaders::GetHeadersError { message } => { Err(report!("Failed to get anisette headers") - .attach(message) - .into()) + .attach(message)) } } } @@ -253,7 +252,7 @@ impl RemoteV3AnisetteProvider { ws_stream .send(Message::Text( serde_json::json!({ - "identifier": BASE64_STANDARD.encode(&state.keychain_identifier), + "identifier": BASE64_STANDARD.encode(state.keychain_identifier), }) .to_string() .into(), @@ -337,15 +336,13 @@ impl RemoteV3AnisetteProvider { ProvisioningMessage::StartProvisioningError { message } => { return Err( report!("Anisette provisioning failed: start provisioning error") - .attach(message) - .into(), + .attach(message), ); } ProvisioningMessage::EndProvisioningError { message } => { return Err( report!("Anisette provisioning failed: end provisioning error") - .attach(message) - .into(), + .attach(message), ); } } diff --git a/isideload/src/anisette/remote_v3/state.rs b/isideload/src/anisette/remote_v3/state.rs index a4aad20..5be5d5a 100644 --- a/isideload/src/anisette/remote_v3/state.rs +++ b/isideload/src/anisette/remote_v3/state.rs @@ -17,7 +17,7 @@ fn bin_serialize_opt(x: &Option>, s: S) -> Result where S: Serializer, { - x.clone().map(|i| Data::new(i)).serialize(s) + x.clone().map(Data::new).serialize(s) } fn bin_deserialize_opt<'de, D>(d: D) -> Result>, D::Error> @@ -71,7 +71,7 @@ impl AnisetteState { pub fn get_md_lu(&self) -> [u8; 32] { let mut hasher = Sha256::new(); - hasher.update(&self.keychain_identifier); + hasher.update(self.keychain_identifier); hasher.finalize().into() } diff --git a/isideload/src/auth/apple_account.rs b/isideload/src/auth/apple_account.rs index d665543..135394a 100644 --- a/isideload/src/auth/apple_account.rs +++ b/isideload/src/auth/apple_account.rs @@ -300,9 +300,9 @@ impl AppleAccount { .await .context("Failed to read SMS 2FA error response text")?; // try to parse as json, if it fails, just bail with the text - if let Ok(json) = serde_json::from_str::(&text) { - if let Some(service_errors) = json.get("serviceErrors") { - if let Some(first_error) = service_errors.as_array().and_then(|arr| arr.get(0)) + if let Ok(json) = serde_json::from_str::(&text) + && let Some(service_errors) = json.get("serviceErrors") + && let Some(first_error) = service_errors.as_array().and_then(|arr| arr.first()) { let code = first_error .get("code") @@ -323,8 +323,6 @@ impl AppleAccount { message ); } - } - } bail!( "SMS 2FA code submission failed with http status {}: {}", status, @@ -435,7 +433,7 @@ impl AppleAccount { let hashed_password = Sha256::digest(password.as_bytes()); let password_hash = if selected_protocol == "s2k_fo" { - hex::encode(&hashed_password).into_bytes() + hex::encode(hashed_password).into_bytes() } else { hashed_password.to_vec() }; @@ -445,7 +443,7 @@ impl AppleAccount { .context("Failed to derive password using PBKDF2")?; let verifier: SrpClientVerifier = srp_client - .process_reply(&a, &self.email.as_bytes(), &password_buf, salt, b_pub) + .process_reply(&a, self.email.as_bytes(), &password_buf, salt, b_pub) .unwrap(); let req2 = plist!(dict { @@ -486,7 +484,7 @@ impl AppleAccount { .get_data("spd") .context("Failed to get SPD from login response")?; - let spd_decrypted = Self::decrypt_cbc(&verifier, &spd_encrypted) + let spd_decrypted = Self::decrypt_cbc(&verifier, spd_encrypted) .context("Failed to decrypt SPD from login response")?; let spd: plist::Dictionary = plist::from_bytes(&spd_decrypted).context("Failed to parse decrypted SPD plist")?; @@ -575,7 +573,7 @@ impl AppleAccount { .get_data("et") .context("Failed to get encrypted token")?; - let decrypted_token = Self::decrypt_gcm(&encrypted_token, &session_key) + let decrypted_token = Self::decrypt_gcm(encrypted_token, session_key) .context("Failed to decrypt app token")?; let token: Dictionary = plist::from_bytes(&decrypted_token) @@ -612,7 +610,7 @@ impl AppleAccount { fn create_session_key(usr: &SrpClientVerifier, name: &str) -> Result, Report> { Ok( - as hmac::Mac>::new_from_slice(&usr.key())? + as hmac::Mac>::new_from_slice(usr.key())? .chain_update(name.as_bytes()) .finalize() .into_bytes() @@ -627,7 +625,7 @@ impl AppleAccount { Ok( cbc::Decryptor::::new_from_slices(&extra_data_key, extra_data_iv)? - .decrypt_padded_vec_mut::(&data)?, + .decrypt_padded_vec_mut::(data)?, ) } diff --git a/isideload/src/auth/grandslam.rs b/isideload/src/auth/grandslam.rs index 150adbb..006d836 100644 --- a/isideload/src/auth/grandslam.rs +++ b/isideload/src/auth/grandslam.rs @@ -105,7 +105,7 @@ impl GrandSlam { ) -> Result { let resp = self .post(url)? - .headers(additional_headers.unwrap_or_else(|| reqwest::header::HeaderMap::new())) + .headers(additional_headers.unwrap_or_else(reqwest::header::HeaderMap::new)) .body(plist_to_xml_string(body)) .send() .await diff --git a/isideload/src/dev/developer_session.rs b/isideload/src/dev/developer_session.rs index 2842719..b28398d 100644 --- a/isideload/src/dev/developer_session.rs +++ b/isideload/src/dev/developer_session.rs @@ -69,7 +69,7 @@ impl DeveloperSession { url: &str, body: impl Into>, ) -> Result<(Dictionary, Option), Report> { - let body = body.into().unwrap_or_else(|| Dictionary::new()); + let body = body.into().unwrap_or_else(Dictionary::new); let base = plist!(dict { "clientId": "XABBG36SBA", @@ -147,11 +147,10 @@ impl DeveloperSession { let result: Result = dict.get_struct(response_key); - if let Err(_) = &result { - if let Some(err) = server_error { + if result.is_err() + && let Some(err) = server_error { bail!(err); } - } Ok(result.context("Failed to extract developer request result")?) } From ae49b22650356d9d9a5051772d9e6119972d4731 Mon Sep 17 00:00:00 2001 From: nab138 Date: Mon, 2 Feb 2026 10:23:57 -0500 Subject: [PATCH 37/71] Return actual enum for DeveloperErrors to allow programatic error handling --- isideload/src/dev/developer_session.rs | 34 ++++++++++++-------------- isideload/src/lib.rs | 3 +++ 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/isideload/src/dev/developer_session.rs b/isideload/src/dev/developer_session.rs index b28398d..a5ee76d 100644 --- a/isideload/src/dev/developer_session.rs +++ b/isideload/src/dev/developer_session.rs @@ -8,6 +8,7 @@ use tracing::{error, warn}; use uuid::Uuid; use crate::{ + SideloadError, anisette::AnisetteDataGenerator, auth::{ apple_account::{AppToken, AppleAccount}, @@ -68,7 +69,7 @@ impl DeveloperSession { &mut self, url: &str, body: impl Into>, - ) -> Result<(Dictionary, Option), Report> { + ) -> Result<(Dictionary, Option), Report> { let body = body.into().unwrap_or_else(Dictionary::new); let base = plist!(dict { @@ -108,27 +109,23 @@ impl DeveloperSession { // 2. We return server errors if the expected data is missing // 3. We return parsing errors if there is no server error but the expected data is missing let response_code = dict.get("resultCode").and_then(|v| v.as_signed_integer()); - let mut server_error: Option = None; + let mut server_error: Option = None; if let Some(code) = response_code { if code != 0 { - let user_string = dict - .get("userString") - .and_then(|v| v.as_string()) - .unwrap_or("Developer request failed."); - let result_string = dict .get("resultString") .and_then(|v| v.as_string()) .unwrap_or("No error message given."); + let user_string = dict + .get("userString") + .and_then(|v| v.as_string()) + .unwrap_or(result_string); + server_error = Some(SideloadError::DeveloperError(code, user_string.to_string())); - // if user and result string match, only show one - if user_string == result_string { - server_error = Some(format!("{} Code: {}", user_string, code)); - } else { - server_error = - Some(format!("{} Code: {}; {}", user_string, code, result_string)); - } - error!(server_error); + error!( + "Developer request returned error code {}: {} ({})", + code, user_string, result_string + ); } } else { warn!("No resultCode in developer request response"); @@ -148,9 +145,10 @@ impl DeveloperSession { let result: Result = dict.get_struct(response_key); if result.is_err() - && let Some(err) = server_error { - bail!(err); - } + && let Some(err) = server_error + { + bail!(err); + } Ok(result.context("Failed to extract developer request result")?) } diff --git a/isideload/src/lib.rs b/isideload/src/lib.rs index 14a1c66..54eb968 100644 --- a/isideload/src/lib.rs +++ b/isideload/src/lib.rs @@ -18,6 +18,9 @@ pub enum SideloadError { #[error("Failed to get anisette data, anisette not provisioned")] AnisetteNotProvisioned, + + #[error("Developer error {0}: {1}")] + DeveloperError(i64, String), } struct ReqwestErrorFormatter; From 1a7e3052973dc8babd39bb6fec0eab9fa370103b Mon Sep 17 00:00:00 2001 From: nab138 Date: Mon, 2 Feb 2026 11:13:31 -0500 Subject: [PATCH 38/71] Start sideload method --- isideload/src/lib.rs | 2 ++ isideload/src/sideload/mod.rs | 16 +++++++++++++ isideload/src/util/device.rs | 44 +++++++++++++++++++++++++++++++++++ isideload/src/util/mod.rs | 1 + 4 files changed, 63 insertions(+) create mode 100644 isideload/src/sideload/mod.rs create mode 100644 isideload/src/util/device.rs diff --git a/isideload/src/lib.rs b/isideload/src/lib.rs index 54eb968..0c73bc3 100644 --- a/isideload/src/lib.rs +++ b/isideload/src/lib.rs @@ -6,6 +6,7 @@ use rootcause::{ pub mod anisette; pub mod auth; pub mod dev; +pub mod sideload; pub mod util; #[derive(Debug, thiserror::Error)] @@ -23,6 +24,7 @@ pub enum SideloadError { DeveloperError(i64, String), } +// The default reqwest error formatter sucks and provides no info struct ReqwestErrorFormatter; impl ContextFormatterHook for ReqwestErrorFormatter { diff --git a/isideload/src/sideload/mod.rs b/isideload/src/sideload/mod.rs new file mode 100644 index 0000000..baaf66d --- /dev/null +++ b/isideload/src/sideload/mod.rs @@ -0,0 +1,16 @@ +use std::path::PathBuf; + +use idevice::provider::IdeviceProvider; +use rootcause::prelude::*; + +use crate::dev::developer_session::DeveloperSession; +use crate::util::device::IdeviceInfo; + +pub async fn sideload_app( + device_provider: &impl IdeviceProvider, + dev_session: &DeveloperSession, + app_path: PathBuf, +) -> Result<(), Report> { + let device_info = IdeviceInfo::from_device(device_provider).await?; + Ok(()) +} diff --git a/isideload/src/util/device.rs b/isideload/src/util/device.rs new file mode 100644 index 0000000..2fbcce9 --- /dev/null +++ b/isideload/src/util/device.rs @@ -0,0 +1,44 @@ +use idevice::{IdeviceService, lockdown::LockdownClient, provider::IdeviceProvider}; +use rootcause::prelude::*; + +pub struct IdeviceInfo { + pub name: String, + pub udid: String, +} + +impl IdeviceInfo { + pub fn new(name: String, udid: String) -> Self { + Self { name, udid } + } + + pub async fn from_device(device: &impl IdeviceProvider) -> Result { + let mut lockdown = LockdownClient::connect(device) + .await + .context("Failed to connect to device lockdown")?; + let pairing = device + .get_pairing_file() + .await + .context("Failed to get device pairing file")?; + lockdown + .start_session(&pairing) + .await + .context("Failed to start lockdown session")?; + let device_name = lockdown + .get_value(Some("DeviceName"), None) + .await + .context("Failed to get device name")? + .as_string() + .ok_or_else(|| report!("Device name is not a string"))? + .to_string(); + + let device_udid = lockdown + .get_value(Some("UniqueDeviceID"), None) + .await + .context("Failed to get device UDID")? + .as_string() + .ok_or_else(|| report!("Device UDID is not a string"))? + .to_string(); + + Ok(Self::new(device_name, device_udid)) + } +} diff --git a/isideload/src/util/mod.rs b/isideload/src/util/mod.rs index 088c202..b6f59a8 100644 --- a/isideload/src/util/mod.rs +++ b/isideload/src/util/mod.rs @@ -1 +1,2 @@ +pub mod device; pub mod plist; From eaf65c8ef95d6c389654fd40ca7fbf7748397b5f Mon Sep 17 00:00:00 2001 From: nab138 Date: Mon, 2 Feb 2026 22:55:01 -0500 Subject: [PATCH 39/71] expand certificate struct --- isideload/src/dev/certificates.rs | 32 ++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/isideload/src/dev/certificates.rs b/isideload/src/dev/certificates.rs index 298da91..0b6b2e9 100644 --- a/isideload/src/dev/certificates.rs +++ b/isideload/src/dev/certificates.rs @@ -3,7 +3,7 @@ use crate::dev::{ device_type::{DeveloperDeviceType, dev_url}, teams::DeveloperTeam, }; -use plist::Data; +use plist::{Data, Date}; use plist_macro::plist; use rootcause::prelude::*; use serde::Deserialize; @@ -16,7 +16,27 @@ pub struct DevelopmentCertificate { pub certificate_id: Option, pub serial_number: Option, pub machine_id: Option, + pub machine_name: Option, pub cert_content: Option, + pub certificate_platform: Option, + pub certificate_type: Option, + pub status: Option, + pub status_code: Option, + pub expiration_date: Option, +} + +#[derive(Deserialize, Clone, Debug)] +#[serde(rename_all = "camelCase")] +pub struct CertificateType { + pub certificate_type_display_id: Option, + pub name: Option, + pub platform: Option, + pub permission_type: Option, + pub distribution_type: Option, + pub distribution_method: Option, + pub owner_type: Option, + pub days_overlap: Option, + pub max_active_certs: Option, } #[derive(Deserialize, Debug, Clone)] @@ -28,11 +48,12 @@ pub struct CertRequest { // the automatic debug implementation spams the console with the cert content bytes impl std::fmt::Debug for DevelopmentCertificate { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("DevelopmentCertificate") - .field("name", &self.name) + let mut s = f.debug_struct("DevelopmentCertificate"); + s.field("name", &self.name) .field("certificate_id", &self.certificate_id) .field("serial_number", &self.serial_number) .field("machine_id", &self.machine_id) + .field("machine_name", &self.machine_name) .field( "cert_content", &self @@ -41,6 +62,11 @@ impl std::fmt::Debug for DevelopmentCertificate { .map(|c| format!("Some([{} bytes])", c.as_ref().len())) .unwrap_or("None".to_string()), ) + .field("certificate_platform", &self.certificate_platform) + .field("certificate_type", &self.certificate_type) + .field("status", &self.status) + .field("status_code", &self.status_code) + .field("expiration_date", &self.expiration_date) .finish() } } From 2f01d80a39ea281feae75d99680321d910b9ad21 Mon Sep 17 00:00:00 2001 From: nab138 Date: Mon, 2 Feb 2026 22:55:14 -0500 Subject: [PATCH 40/71] continue implimenting sideloading --- Cargo.lock | 38 +++++++++++++++++ examples/minimal/Cargo.toml | 1 + examples/minimal/src/main.rs | 73 ++++++++++++++++++++------------ isideload/Cargo.toml | 2 +- isideload/src/dev/devices.rs | 22 ++++++++++ isideload/src/sideload/config.rs | 46 ++++++++++++++++++++ isideload/src/sideload/mod.rs | 39 ++++++++++++++++- 7 files changed, 192 insertions(+), 29 deletions(-) create mode 100644 isideload/src/sideload/config.rs diff --git a/Cargo.lock b/Cargo.lock index d23a994..8a97a6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -392,6 +392,21 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.31" @@ -399,6 +414,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -407,6 +423,23 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + [[package]] name = "futures-macro" version = "0.3.31" @@ -436,10 +469,13 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ + "futures-channel", "futures-core", + "futures-io", "futures-macro", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -733,6 +769,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4031af51250d2f22f61a0d7fb7ea71ba8b6144b2b9dd3b7ee4a931fccbd1ec0" dependencies = [ "base64", + "futures", "plist", "plist-macro", "rustls", @@ -924,6 +961,7 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" name = "minimal" version = "0.1.0" dependencies = [ + "idevice", "isideload", "plist", "plist-macro", diff --git a/examples/minimal/Cargo.toml b/examples/minimal/Cargo.toml index 8b737d3..89f7a4d 100644 --- a/examples/minimal/Cargo.toml +++ b/examples/minimal/Cargo.toml @@ -10,3 +10,4 @@ plist-macro = "0.1.3" tokio = { version = "1.49.0", features = ["rt-multi-thread", "macros"] } tracing = "0.1.44" tracing-subscriber = "0.3.22" +idevice = { version = "0.1.52", features = ["usbmuxd"]} \ No newline at end of file diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 03f1caa..eb40aee 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -1,12 +1,11 @@ -use std::env; +use std::{env, path::PathBuf}; +use idevice::usbmuxd::{UsbmuxdAddr, UsbmuxdConnection}; use isideload::{ anisette::remote_v3::RemoteV3AnisetteProvider, auth::apple_account::AppleAccount, - dev::{ - certificates::CertificatesApi, - developer_session::{DeveloperSession, TeamsApi}, - }, + dev::developer_session::DeveloperSession, + sideload::{SideloadConfiguration, TeamSelection, sideload_app}, }; use tracing::Level; @@ -21,14 +20,15 @@ async fn main() { tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); let args: Vec = env::args().collect(); - // let _app_path = PathBuf::from( - // args.get(1) - // .expect("Please provide the path to the app to install"), - // ); + let apple_id = args .get(1) .expect("Please provide the Apple ID to use for installation"); let apple_password = args.get(2).expect("Please provide the Apple ID password"); + let app_path = PathBuf::from( + args.get(3) + .expect("Please provide the path to the app to install"), + ); let get_2fa_code = || { let mut code = String::new(); @@ -53,25 +53,46 @@ async fn main() { .await .expect("Failed to create developer session"); - let teams = dev_session - .list_teams() - .await - .expect("Failed to list teams"); + let usbmuxd = UsbmuxdConnection::default().await; + if usbmuxd.is_err() { + panic!("Failed to connect to usbmuxd: {:?}", usbmuxd.err()); + } + let mut usbmuxd = usbmuxd.unwrap(); - let team = teams - .get(0) - .expect("No developer teams available for this account"); + let devs = usbmuxd.get_devices().await.unwrap(); + if devs.is_empty() { + panic!("No devices found"); + } - // let app_ids = dev_session - // .list_app_ids(team, None) - // .await - // .expect("Failed to add appid"); - // let app_id = app_ids.app_ids.get(0).cloned().unwrap(); + let provider = devs + .iter() + .next() + .unwrap() + .to_provider(UsbmuxdAddr::from_env_var().unwrap(), "isideload-demo"); - let res = dev_session - .list_all_development_certs(team, None) - .await - .expect("Failed to list dev certs"); + let sideload_config = + SideloadConfiguration::builder().team_selection(TeamSelection::Prompt(|teams| { + println!("Please select a team:"); + for (index, team) in teams.iter().enumerate() { + println!( + "{}: {} ({})", + index + 1, + team.name.as_deref().unwrap_or(""), + team.team_id + ); + } + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + let selection = input.trim().parse::().ok()?; + if selection == 0 || selection > teams.len() { + return None; + } + Some(teams[selection - 1].team_id.clone()) + })); - println!("{:?}", res); + let result = sideload_app(&provider, &mut dev_session, app_path, &sideload_config).await; + match result { + Ok(_) => println!("App installed successfully"), + Err(e) => panic!("Failed to install app: {:?}", e), + } } diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 45726e5..53a4db4 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -15,7 +15,7 @@ default = ["install"] install = ["dep:idevice"] [dependencies] -idevice = { version = "0.1.51", optional = true } +idevice = { version = "0.1.52", optional = true } plist = "1.8" plist-macro = "0.1.3" reqwest = { version = "0.13.1", features = ["json", "gzip"] } diff --git a/isideload/src/dev/devices.rs b/isideload/src/dev/devices.rs index 6c57559..4a5c37d 100644 --- a/isideload/src/dev/devices.rs +++ b/isideload/src/dev/devices.rs @@ -6,6 +6,7 @@ use crate::dev::{ use plist_macro::plist; use rootcause::prelude::*; use serde::Deserialize; +use tracing::info; #[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] @@ -59,6 +60,27 @@ pub trait DevicesApi { Ok(device) } + + // TODO: This can be skipped if we know the device is already registered + /// Check if the device is a development device, and add it if not + async fn ensure_device_registered( + &mut self, + team: &DeveloperTeam, + name: &str, + udid: &str, + device_type: impl Into> + Send, + ) -> Result<(), Report> { + let device_type = device_type.into(); + let devices = self.list_devices(team, device_type.clone()).await?; + + if !devices.iter().any(|d| d.device_number == udid) { + info!("Registering development device"); + self.add_device(team, name, udid, device_type).await?; + } + info!("Device is a development device"); + + Ok(()) + } } impl DevicesApi for DeveloperSession { diff --git a/isideload/src/sideload/config.rs b/isideload/src/sideload/config.rs new file mode 100644 index 0000000..a09f075 --- /dev/null +++ b/isideload/src/sideload/config.rs @@ -0,0 +1,46 @@ +use std::fmt::Display; + +use crate::dev::teams::DeveloperTeam; + +/// Configuration for selecting a developer team during sideloading +/// +/// If there is only one team, it will be selected automatically regardless of this setting. +/// If there are multiple teams, the behavior will depend on this setting. +pub enum TeamSelection { + /// Select the first team automatically + First, + /// Prompt the user to select a team + Prompt(fn(&Vec) -> Option), +} + +impl Display for TeamSelection { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TeamSelection::First => write!(f, "first team"), + TeamSelection::Prompt(_) => write!(f, "prompting for team"), + } + } +} + +pub struct SideloadConfiguration { + pub team_selection: TeamSelection, +} + +impl Default for SideloadConfiguration { + fn default() -> Self { + SideloadConfiguration { + team_selection: TeamSelection::First, + } + } +} + +impl SideloadConfiguration { + pub fn builder() -> Self { + Self::default() + } + + pub fn team_selection(mut self, selection: TeamSelection) -> Self { + self.team_selection = selection; + self + } +} diff --git a/isideload/src/sideload/mod.rs b/isideload/src/sideload/mod.rs index baaf66d..eb0af97 100644 --- a/isideload/src/sideload/mod.rs +++ b/isideload/src/sideload/mod.rs @@ -2,15 +2,50 @@ use std::path::PathBuf; use idevice::provider::IdeviceProvider; use rootcause::prelude::*; +use tracing::info; -use crate::dev::developer_session::DeveloperSession; +use crate::dev::teams::TeamsApi; +use crate::dev::{developer_session::DeveloperSession, devices::DevicesApi}; use crate::util::device::IdeviceInfo; +pub mod config; +pub use config::{SideloadConfiguration, TeamSelection}; + pub async fn sideload_app( device_provider: &impl IdeviceProvider, - dev_session: &DeveloperSession, + dev_session: &mut DeveloperSession, app_path: PathBuf, + config: &SideloadConfiguration, ) -> Result<(), Report> { let device_info = IdeviceInfo::from_device(device_provider).await?; + + let teams = dev_session.list_teams().await?; + let team = match teams.len() { + 0 => { + bail!("No developer teams available") + } + 1 => &teams[0], + _ => { + info!( + "Multiple developer teams found, {} as per configuration", + config.team_selection + ); + match &config.team_selection { + TeamSelection::First => &teams[0], + TeamSelection::Prompt(prompt_fn) => { + let selection = prompt_fn(&teams).ok_or_else(|| report!("No team selected"))?; + teams + .iter() + .find(|t| t.team_id == selection) + .ok_or_else(|| report!("No team found with ID {}", selection))? + } + } + } + }; + + dev_session + .ensure_device_registered(team, &device_info.name, &device_info.udid, None) + .await?; + Ok(()) } From 7ed69b3e710ee6f722958de089da911e028d6e52 Mon Sep 17 00:00:00 2001 From: nab138 Date: Wed, 4 Feb 2026 13:15:31 -0500 Subject: [PATCH 41/71] Use prerelease version of srp to avoid using my fork --- Cargo.lock | 479 +++++++++++++++++++++------- isideload/Cargo.toml | 14 +- isideload/src/auth/apple_account.rs | 85 +++-- 3 files changed, 415 insertions(+), 163 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a97a6c..5dbd6a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,30 +10,30 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aead" -version = "0.5.2" +version = "0.6.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +checksum = "6b657e772794c6b04730ea897b66a058ccd866c16d1967da05eeeecec39043fe" dependencies = [ - "crypto-common", - "generic-array 0.14.7", + "crypto-common 0.2.0", + "inout", ] [[package]] name = "aes" -version = "0.8.4" +version = "0.9.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +checksum = "04097e08a47d9ad181c2e1f4a5fabc9ae06ce8839a333ba9a949bcb0d31fd2a3" dependencies = [ - "cfg-if", "cipher", + "cpubits", "cpufeatures", ] [[package]] name = "aes-gcm" -version = "0.10.3" +version = "0.11.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +checksum = "e22c0c90bbe8d4f77c3ca9ddabe41a1f8382d6fc1f7cea89459d0f320371f972" dependencies = [ "aead", "aes", @@ -43,6 +43,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + [[package]] name = "async-compression" version = "0.4.37" @@ -100,6 +106,12 @@ dependencies = [ "fs_extra", ] +[[package]] +name = "base16ct" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6" + [[package]] name = "base64" version = "0.22.1" @@ -124,16 +136,25 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.7", + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96eb4cdd6cf1b31d671e9efe75c5d1ec614776856cefbe109ca373554a6d514f" +dependencies = [ + "hybrid-array", ] [[package]] name = "block-padding" -version = "0.3.3" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +checksum = "710f1dd022ef4e93f8a438b4ba958de7f64308434fa6a87104481645cc30068b" dependencies = [ - "generic-array 0.14.7", + "hybrid-array", ] [[package]] @@ -150,9 +171,9 @@ checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "cbc" -version = "0.1.2" +version = "0.2.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +checksum = "85742c5f1d0dda799d2e582c76b82b817d3e4d6434dd285e48e90ed0c963b667" dependencies = [ "cipher", ] @@ -189,11 +210,12 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "cipher" -version = "0.4.4" +version = "0.5.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +checksum = "9002c8edb9b1e21938663da3489c9c4403bba2393997fb2ecbd401386c0e71dc" dependencies = [ - "crypto-common", + "block-buffer 0.11.0", + "crypto-common 0.2.0", "inout", ] @@ -206,6 +228,12 @@ dependencies = [ "cc", ] +[[package]] +name = "cmov" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0758edba32d61d1fd9f4d69491b47604b91ee2f7e6b33de7e54ca4ebe55dc3" + [[package]] name = "combine" version = "4.6.7" @@ -233,6 +261,12 @@ version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + [[package]] name = "core-foundation" version = "0.9.4" @@ -259,6 +293,12 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpubits" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef0c543070d296ea414df2dd7625d1b24866ce206709d8a4a424f28377f5861" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -277,26 +317,59 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crypto-bigint" +version = "0.7.0-rc.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cba9eeeb213f7fd29353032f71f7c173e5f6d95d85151cb3a47197b0ea7e8be7" +dependencies = [ + "cpubits", + "ctutils", + "getrandom 0.4.1", + "num-traits", + "rand_core 0.10.0", + "serdect", +] + [[package]] name = "crypto-common" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", + "generic-array", "typenum", ] [[package]] -name = "ctr" -version = "0.9.2" +name = "crypto-common" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +checksum = "211f05e03c7d03754740fd9e585de910a095d6b99f8bcfffdef8319fa02a8331" +dependencies = [ + "getrandom 0.4.1", + "hybrid-array", + "rand_core 0.10.0", +] + +[[package]] +name = "ctr" +version = "0.10.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65ea71550d18331d179854662ab330bb54306b9b56020d0466aae2a58f4e17c1" dependencies = [ "cipher", ] +[[package]] +name = "ctutils" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1005a6d4446f5120ef475ad3d2af2b30c49c2c9c6904258e3bb30219bebed5e4" +dependencies = [ + "cmov", +] + [[package]] name = "data-encoding" version = "2.10.0" @@ -318,8 +391,19 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "crypto-common", + "block-buffer 0.10.4", + "crypto-common 0.1.7", +] + +[[package]] +name = "digest" +version = "0.11.0-rc.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b42f1d9edf5207c137646b568a0168ca0ec25b7f9eaf7f9961da51a3d91cea" +dependencies = [ + "block-buffer 0.11.0", + "const-oid", + "crypto-common 0.2.0", "subtle", ] @@ -377,6 +461,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -491,16 +581,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "generic-array" -version = "1.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf57c49a95fd1fe24b90b3033bee6dc7e8f1288d51494cb44e627c295e38542" -dependencies = [ - "rustversion", - "typenum", -] - [[package]] name = "getrandom" version = "0.2.17" @@ -529,12 +609,25 @@ dependencies = [ ] [[package]] -name = "ghash" -version = "0.5.1" +name = "getrandom" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "rand_core 0.10.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "ghash" +version = "0.6.0-rc.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f484be0236661c5ba22d445ed75d3624ba5544541c647549f867fb576e55b2a2" dependencies = [ - "opaque-debug", "polyval", ] @@ -557,12 +650,27 @@ dependencies = [ "tracing", ] +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + [[package]] name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hex" version = "0.4.3" @@ -571,11 +679,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hmac" -version = "0.12.1" +version = "0.13.0-rc.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +checksum = "ef451d73f36d8a3f93ad32c332ea01146c9650e1ec821a9b0e46c01277d544f8" dependencies = [ - "digest", + "digest 0.11.0-rc.11", ] [[package]] @@ -617,6 +725,15 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "hybrid-array" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b229d73f5803b562cc26e4da0396c8610a4ee209f4fac8fa4f8d709166dc45" +dependencies = [ + "typenum", +] + [[package]] name = "hyper" version = "1.8.1" @@ -762,6 +879,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "idevice" version = "0.1.52" @@ -808,17 +931,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.16.1", + "serde", + "serde_core", ] [[package]] name = "inout" -version = "0.1.4" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" dependencies = [ "block-padding", - "generic-array 0.14.7", + "hybrid-array", ] [[package]] @@ -850,7 +975,6 @@ dependencies = [ "hex", "hmac", "idevice", - "nab138_srp", "pbkdf2", "plist", "plist-macro", @@ -860,6 +984,7 @@ dependencies = [ "serde", "serde_json", "sha2", + "srp", "thiserror 2.0.18", "tokio", "tokio-tungstenite", @@ -921,6 +1046,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" version = "0.2.180" @@ -991,20 +1122,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "nab138_srp" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "587a7a2ae38ab9a818f42c12b02a7ad5d738006f78f3b53a9f28da91fe13411d" -dependencies = [ - "base64", - "digest", - "generic-array 1.3.5", - "lazy_static", - "num-bigint", - "subtle", -] - [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -1014,31 +1131,12 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -1054,12 +1152,6 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - [[package]] name = "openssl-probe" version = "0.2.1" @@ -1068,11 +1160,11 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "pbkdf2" -version = "0.12.2" +version = "0.13.0-rc.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +checksum = "c8dfa4e14084d963d35bfb4cdb38712cde78dcf83054c0e8b9b8e899150f374e" dependencies = [ - "digest", + "digest 0.11.0-rc.11", "hmac", ] @@ -1117,13 +1209,12 @@ dependencies = [ [[package]] name = "polyval" -version = "0.6.2" +version = "0.7.0-rc.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +checksum = "63641a86fddf4b5274f31c43734458ec7acd3133016dbaa37e4e247e1e9acd46" dependencies = [ - "cfg-if", + "cpubits", "cpufeatures", - "opaque-debug", "universal-hash", ] @@ -1151,6 +1242,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -1260,15 +1361,6 @@ dependencies = [ "rand_core 0.9.5", ] -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] - [[package]] name = "rand_core" version = "0.9.5" @@ -1278,6 +1370,12 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rand_core" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" + [[package]] name = "reqwest" version = "0.13.1" @@ -1338,7 +1436,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a751633dcb95a6b1c954f0fa15c2afd9b4802640f8045432f68a1f4bde4b871" dependencies = [ - "hashbrown", + "hashbrown 0.16.1", "indexmap", "rootcause-internals", "rustc-hash", @@ -1480,6 +1578,12 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + [[package]] name = "serde" version = "1.0.228" @@ -1523,6 +1627,16 @@ dependencies = [ "zmij", ] +[[package]] +name = "serdect" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9af4a3e75ebd5599b30d4de5768e00b5095d518a79fefc3ecbaf77e665d1ec06" +dependencies = [ + "base16ct", + "serde", +] + [[package]] name = "sha1" version = "0.10.6" @@ -1531,18 +1645,18 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] name = "sha2" -version = "0.10.9" +version = "0.11.0-rc.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +checksum = "7c5f3b1e2dc8aad28310d8410bd4d7e180eca65fca176c52ab00d364475d0024" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.11.0-rc.11", ] [[package]] @@ -1588,6 +1702,18 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "srp" +version = "0.7.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b7d0b0d27b2475e779315c09af698ac9f6d40016bc011bf3fa0f0054ce38ed" +dependencies = [ + "crypto-bigint", + "crypto-common 0.2.0", + "digest 0.11.0-rc.11", + "subtle", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -1973,12 +2099,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] -name = "universal-hash" -version = "0.5.1" +name = "unicode-xid" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "universal-hash" +version = "0.6.0-rc.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058482a494bb3c9c39447d8b40a3a0f38ebb3dccaf02c5a2d681e69035f8da11" dependencies = [ - "crypto-common", + "crypto-common 0.2.0", "subtle", ] @@ -2069,6 +2201,15 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" version = "0.2.108" @@ -2128,6 +2269,40 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.10.0", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + [[package]] name = "web-sys" version = "0.3.85" @@ -2446,6 +2621,88 @@ name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.10.0", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "writeable" diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 53a4db4..199e2ee 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -24,7 +24,7 @@ async-trait = "0.1.89" serde = "1.0.228" rand = "0.9.2" uuid = {version = "1.20.0", features = ["v4"] } -sha2 = "0.10.9" +sha2 = "0.11.0-rc.4" tracing = "0.1.44" tokio-tungstenite = { version = "0.28.0", features = ["rustls-tls-webpki-roots"] } rootcause = "0.11.1" @@ -32,10 +32,10 @@ futures-util = "0.3.31" serde_json = "1.0.149" base64 = "0.22.1" hex = "0.4.3" -srp = { package = "nab138_srp", version = "0.6.0" } -pbkdf2 = "0.12.2" -hmac = "0.12.1" -cbc = { version = "0.1.2", features = ["std"] } -aes = "0.8.4" -aes-gcm = "0.10.3" +srp = "0.7.0-rc.1" +pbkdf2 = "0.13.0-rc.9" +hmac = "0.13.0-rc.5" +cbc = { version = "0.2.0-rc.3", features = ["alloc"] } +aes = "0.9.0-rc.4" +aes-gcm = "0.11.0-rc.3" tokio = "1.49.0" diff --git a/isideload/src/auth/apple_account.rs b/isideload/src/auth/apple_account.rs index 135394a..2524cb8 100644 --- a/isideload/src/auth/apple_account.rs +++ b/isideload/src/auth/apple_account.rs @@ -12,19 +12,16 @@ use aes::{ Aes256, cipher::{block_padding::Pkcs7, consts::U16}, }; -use aes_gcm::{AeadInPlace, AesGcm, KeyInit, Nonce}; +use aes_gcm::{AeadInOut, AesGcm, KeyInit, Nonce}; use base64::{Engine, prelude::BASE64_STANDARD}; -use cbc::cipher::{BlockDecryptMut, KeyIvInit}; -use hmac::Mac; +use cbc::cipher::{BlockModeDecrypt, KeyIvInit}; +use hmac::{Hmac, Mac}; use plist::Dictionary; use plist_macro::plist; use reqwest::header::{HeaderMap, HeaderValue}; use rootcause::prelude::*; use sha2::{Digest, Sha256}; -use srp::{ - client::{SrpClient, SrpClientVerifier}, - groups::G_2048, -}; +use srp::{ClientVerifier, groups::G2048}; use tracing::{debug, info, warn}; pub struct AppleAccount { @@ -302,27 +299,27 @@ impl AppleAccount { // try to parse as json, if it fails, just bail with the text if let Ok(json) = serde_json::from_str::(&text) && let Some(service_errors) = json.get("serviceErrors") - && let Some(first_error) = service_errors.as_array().and_then(|arr| arr.first()) - { - let code = first_error - .get("code") - .and_then(|c| c.as_str()) - .unwrap_or("unknown"); - let title = first_error - .get("title") - .and_then(|t| t.as_str()) - .unwrap_or("No title provided"); - let message = first_error - .get("message") - .and_then(|m| m.as_str()) - .unwrap_or("No message provided"); - bail!( - "SMS 2FA code submission failed (code {}): {} - {}", - code, - title, - message - ); - } + && let Some(first_error) = service_errors.as_array().and_then(|arr| arr.first()) + { + let code = first_error + .get("code") + .and_then(|c| c.as_str()) + .unwrap_or("unknown"); + let title = first_error + .get("title") + .and_then(|t| t.as_str()) + .unwrap_or("No title provided"); + let message = first_error + .get("message") + .and_then(|m| m.as_str()) + .unwrap_or("No message provided"); + bail!( + "SMS 2FA code submission failed (code {}): {} - {}", + code, + title, + message + ); + } bail!( "SMS 2FA code submission failed with http status {}: {}", status, @@ -373,7 +370,7 @@ impl AppleAccount { let cpd = anisette_data.get_client_provided_data(); - let srp_client = SrpClient::::new(&G_2048); + let srp_client = srp::Client::::new_with_options(false); let a: Vec = (0..32).map(|_| rand::random::()).collect(); let a_pub = srp_client.compute_public_ephemeral(&a); @@ -442,7 +439,7 @@ impl AppleAccount { pbkdf2::pbkdf2::>(&password_hash, salt, iters as u32, &mut password_buf) .context("Failed to derive password using PBKDF2")?; - let verifier: SrpClientVerifier = srp_client + let verifier = srp_client .process_reply(&a, self.email.as_bytes(), &password_buf, salt, b_pub) .unwrap(); @@ -534,7 +531,7 @@ impl AppleAccount { let session_key = spd.get_data("sk").context("Failed to get app token")?; let c = spd.get_data("c").context("Failed to get app token")?; - let checksum = as hmac::Mac>::new_from_slice(session_key) + let checksum = Hmac::::new_from_slice(session_key) .unwrap() .chain_update("apptokens".as_bytes()) .chain_update(dsid.as_bytes()) @@ -608,24 +605,22 @@ impl AppleAccount { Ok(app_token) } - fn create_session_key(usr: &SrpClientVerifier, name: &str) -> Result, Report> { - Ok( - as hmac::Mac>::new_from_slice(usr.key())? - .chain_update(name.as_bytes()) - .finalize() - .into_bytes() - .to_vec(), - ) + fn create_session_key(usr: &ClientVerifier, name: &str) -> Result, Report> { + Ok(Hmac::::new_from_slice(usr.key())? + .chain_update(name.as_bytes()) + .finalize() + .into_bytes() + .to_vec()) } - fn decrypt_cbc(usr: &SrpClientVerifier, data: &[u8]) -> Result, Report> { + fn decrypt_cbc(usr: &ClientVerifier, data: &[u8]) -> Result, Report> { let extra_data_key = Self::create_session_key(usr, "extra data key:")?; let extra_data_iv = Self::create_session_key(usr, "extra data iv:")?; let extra_data_iv = &extra_data_iv[..16]; Ok( cbc::Decryptor::::new_from_slices(&extra_data_key, extra_data_iv)? - .decrypt_padded_vec_mut::(data)?, + .decrypt_padded_vec::(data)?, ) } @@ -653,14 +648,14 @@ impl AppleAccount { bail!("IV is not the correct length: {} bytes", iv.len()); } - let key = aes_gcm::Key::>::from_slice(key); - let cipher = AesGcm::::new(key); - let nonce = Nonce::::from_slice(iv); + let key = aes_gcm::Key::>::try_from(key)?; + let cipher = AesGcm::::new(&key); + let nonce = Nonce::::try_from(iv)?; let mut buf = ciphertext_and_tag.to_vec(); cipher - .decrypt_in_place(nonce, header, &mut buf) + .decrypt_in_place(&nonce, header, &mut buf) .map_err(|e| report!("Failed to decrypt gcm: {}", e))?; Ok(buf) From bad2108e54626b0793e971a7c9428cd4759275bc Mon Sep 17 00:00:00 2001 From: nab138 Date: Thu, 5 Feb 2026 09:05:03 -0500 Subject: [PATCH 42/71] updates more deps --- Cargo.lock | 105 ++++++++++++++++++++----------------------- isideload/Cargo.toml | 2 +- 2 files changed, 50 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5dbd6a3..5bbea23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,9 +86,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.15.3" +version = "1.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e84ce723ab67259cfeb9877c6a639ee9eb7a27b28123abd71db7f0d5d0cc9d86" +checksum = "7b7b6141e96a8c160799cc2d5adecd5cbbe5054cb8c7c4af53da0f83bb7ad256" dependencies = [ "aws-lc-sys", "zeroize", @@ -96,9 +96,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.36.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a442ece363113bd4bd4c8b18977a7798dd4d3c3383f34fb61936960e8f4ad8" +checksum = "5c34dda4df7017c8db52132f0f8a2e0f8161649d15723ed63fc00c82d0f2081a" dependencies = [ "cc", "cmake", @@ -118,12 +118,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.10.0" @@ -165,9 +159,9 @@ checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "bytes" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cbc" @@ -180,9 +174,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.53" +version = "1.2.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" +checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29" dependencies = [ "find-msvc-tools", "jobserver", @@ -210,9 +204,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "cipher" -version = "0.5.0-rc.8" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9002c8edb9b1e21938663da3489c9c4403bba2393997fb2ecbd401386c0e71dc" +checksum = "64727038c8c5e2bb503a15b9f5b9df50a1da9a33e83e1f93067d914f2c6604a5" dependencies = [ "block-buffer 0.11.0", "crypto-common 0.2.0", @@ -441,15 +435,15 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "find-msvc-tools" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "flate2" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", @@ -774,14 +768,13 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "base64", "bytes", "futures-channel", - "futures-core", "futures-util", "http", "http-body", @@ -1133,9 +1126,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" [[package]] name = "num-traits" @@ -1202,7 +1195,7 @@ dependencies = [ [[package]] name = "plist-macro" version = "0.1.3" -source = "git+https://github.com/nab138/plist_macro?branch=master#7004b1625aa50044bd381ee5393a9c85cec039d5" +source = "git+https://github.com/nab138/plist_macro?branch=master#b664a2bc4724d8c5e27a888747400849215c23f0" dependencies = [ "plist", ] @@ -1561,7 +1554,7 @@ version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ - "bitflags 2.10.0", + "bitflags", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -1682,9 +1675,9 @@ checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "slab" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" @@ -1694,9 +1687,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" dependencies = [ "libc", "windows-sys 0.60.2", @@ -1759,20 +1752,20 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.5.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 1.3.2", + "bitflags", "core-foundation 0.9.4", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", @@ -1829,9 +1822,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.45" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", @@ -1844,15 +1837,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.25" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" dependencies = [ "num-conv", "time-core", @@ -1970,7 +1963,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "async-compression", - "bitflags 2.10.0", + "bitflags", "bytes", "futures-core", "futures-util", @@ -2297,7 +2290,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.10.0", + "bitflags", "hashbrown 0.15.5", "indexmap", "semver", @@ -2325,9 +2318,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" dependencies = [ "rustls-pki-types", ] @@ -2338,14 +2331,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.5", + "webpki-roots 1.0.6", ] [[package]] name = "webpki-roots" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" dependencies = [ "rustls-pki-types", ] @@ -2674,7 +2667,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.10.0", + "bitflags", "indexmap", "log", "serde", @@ -2735,18 +2728,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.33" +version = "0.8.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +checksum = "57cf3aa6855b23711ee9852dfc97dfaa51c45feaba5b645d0c777414d494a961" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.33" +version = "0.8.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" +checksum = "8a616990af1a287837c4fe6596ad77ef57948f787e46ce28e166facc0cc1cb75" dependencies = [ "proc-macro2", "quote", @@ -2815,6 +2808,6 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.16" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65" +checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445" diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 199e2ee..5a74e0d 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -24,7 +24,7 @@ async-trait = "0.1.89" serde = "1.0.228" rand = "0.9.2" uuid = {version = "1.20.0", features = ["v4"] } -sha2 = "0.11.0-rc.4" +sha2 = "0.11.0-rc.5" tracing = "0.1.44" tokio-tungstenite = { version = "0.28.0", features = ["rustls-tls-webpki-roots"] } rootcause = "0.11.1" From 27de2210ecb5119522348a7a274b99840e0c287e Mon Sep 17 00:00:00 2001 From: nab138 Date: Thu, 5 Feb 2026 18:16:21 -0500 Subject: [PATCH 43/71] start implimenting cert stuff --- Cargo.lock | 11 +++++++++++ isideload/Cargo.toml | 4 +++- isideload/src/anisette/remote_v3/mod.rs | 5 ++--- isideload/src/sideload/certificate.rs | 6 ++++++ isideload/src/sideload/mod.rs | 2 +- isideload/src/util/keyring_storage.rs | 0 isideload/src/util/mod.rs | 3 +++ isideload/src/util/storage.rs | 21 +++++++++++++++++++++ 8 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 isideload/src/sideload/certificate.rs create mode 100644 isideload/src/util/keyring_storage.rs create mode 100644 isideload/src/util/storage.rs diff --git a/Cargo.lock b/Cargo.lock index 5bbea23..1d69ccd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -968,6 +968,7 @@ dependencies = [ "hex", "hmac", "idevice", + "keyring", "pbkdf2", "plist", "plist-macro", @@ -1033,6 +1034,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keyring" +version = "3.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c" +dependencies = [ + "log", + "zeroize", +] + [[package]] name = "lazy_static" version = "1.5.0" diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 5a74e0d..9af6964 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -11,8 +11,9 @@ keywords = ["ios", "sideload"] readme = "../README.md" [features] -default = ["install"] +default = ["install", "keyring-storage"] install = ["dep:idevice"] +keyring-storage = ["keyring"] [dependencies] idevice = { version = "0.1.52", optional = true } @@ -39,3 +40,4 @@ cbc = { version = "0.2.0-rc.3", features = ["alloc"] } aes = "0.9.0-rc.4" aes-gcm = "0.11.0-rc.3" tokio = "1.49.0" +keyring = { version = "3.6.3", optional = true } diff --git a/isideload/src/anisette/remote_v3/mod.rs b/isideload/src/anisette/remote_v3/mod.rs index 1677544..3665a81 100644 --- a/isideload/src/anisette/remote_v3/mod.rs +++ b/isideload/src/anisette/remote_v3/mod.rs @@ -20,7 +20,7 @@ use crate::auth::grandslam::GrandSlam; use crate::util::plist::PlistDataExtract; use futures_util::{SinkExt, StreamExt}; -pub const DEFAULT_ANISETTE_V3_URL: &str = "https://ani.sidestore.io"; +pub const DEFAULT_ANISETTE_V3_URL: &str = "https://ani.stikstore.app"; pub struct RemoteV3AnisetteProvider { pub state: Option, @@ -127,8 +127,7 @@ impl AnisetteProvider for RemoteV3AnisetteProvider { Ok(data) } AnisetteHeaders::GetHeadersError { message } => { - Err(report!("Failed to get anisette headers") - .attach(message)) + Err(report!("Failed to get anisette headers").attach(message)) } } } diff --git a/isideload/src/sideload/certificate.rs b/isideload/src/sideload/certificate.rs new file mode 100644 index 0000000..c56d52b --- /dev/null +++ b/isideload/src/sideload/certificate.rs @@ -0,0 +1,6 @@ +pub struct CertificateIdentity { + pub machine_id: String, + pub machine_name: String, +} + +impl CertificateIdentity {} diff --git a/isideload/src/sideload/mod.rs b/isideload/src/sideload/mod.rs index eb0af97..ecf254f 100644 --- a/isideload/src/sideload/mod.rs +++ b/isideload/src/sideload/mod.rs @@ -8,6 +8,7 @@ use crate::dev::teams::TeamsApi; use crate::dev::{developer_session::DeveloperSession, devices::DevicesApi}; use crate::util::device::IdeviceInfo; +pub mod certificate; pub mod config; pub use config::{SideloadConfiguration, TeamSelection}; @@ -18,7 +19,6 @@ pub async fn sideload_app( config: &SideloadConfiguration, ) -> Result<(), Report> { let device_info = IdeviceInfo::from_device(device_provider).await?; - let teams = dev_session.list_teams().await?; let team = match teams.len() { 0 => { diff --git a/isideload/src/util/keyring_storage.rs b/isideload/src/util/keyring_storage.rs new file mode 100644 index 0000000..e69de29 diff --git a/isideload/src/util/mod.rs b/isideload/src/util/mod.rs index b6f59a8..41d1f65 100644 --- a/isideload/src/util/mod.rs +++ b/isideload/src/util/mod.rs @@ -1,2 +1,5 @@ pub mod device; +#[cfg(feature = "keyring-storage")] +pub mod keyring_storage; pub mod plist; +pub mod storage; diff --git a/isideload/src/util/storage.rs b/isideload/src/util/storage.rs new file mode 100644 index 0000000..109d554 --- /dev/null +++ b/isideload/src/util/storage.rs @@ -0,0 +1,21 @@ +use base64::prelude::*; +use rootcause::prelude::*; + +pub trait SideloadingStorage: Send + Sync { + fn store(&self, key: &str, value: &str) -> Result<(), Report>; + fn retrieve(&self, key: &str) -> Result, Report>; + + fn store_data(&self, key: &str, value: &[u8]) -> Result<(), Report> { + let encoded = BASE64_STANDARD.encode(value); + self.store(key, &encoded) + } + + fn retrieve_data(&self, key: &str) -> Result>, Report> { + if let Some(encoded) = self.retrieve(key)? { + let decoded = BASE64_STANDARD.decode(&encoded)?; + Ok(Some(decoded)) + } else { + Ok(None) + } + } +} From 6d0f644e9368c88ad60a1334b6320c32ccd4ce12 Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 6 Feb 2026 08:30:59 -0500 Subject: [PATCH 44/71] start refactoring sideloader --- Cargo.lock | 98 ++++++++++++++++++- examples/minimal/src/main.rs | 41 ++++---- isideload/Cargo.toml | 2 +- .../src/sideload/{config.rs => builder.rs} | 19 ++-- isideload/src/sideload/mod.rs | 53 +--------- isideload/src/sideload/sideloader.rs | 66 +++++++++++++ isideload/src/util/keyring_storage.rs | 36 +++++++ isideload/src/util/storage.rs | 47 +++++++++ 8 files changed, 282 insertions(+), 80 deletions(-) rename isideload/src/sideload/{config.rs => builder.rs} (68%) create mode 100644 isideload/src/sideload/sideloader.rs diff --git a/Cargo.lock b/Cargo.lock index 1d69ccd..31e7245 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -157,6 +157,12 @@ version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.11.1" @@ -370,6 +376,27 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +[[package]] +name = "dbus" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b3aa68d7e7abee336255bd7248ea965cc393f3e70411135a6f6a4b651345d4" +dependencies = [ + "libc", + "libdbus-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "dbus-secret-service" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "708b509edf7889e53d7efb0ffadd994cc6c2345ccb62f55cfd6b0682165e4fa6" +dependencies = [ + "dbus", + "zeroize", +] + [[package]] name = "deranged" version = "0.5.5" @@ -1040,7 +1067,13 @@ version = "3.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c" dependencies = [ + "byteorder", + "dbus-secret-service", + "linux-keyutils", "log", + "security-framework 2.11.1", + "security-framework 3.5.1", + "windows-sys 0.60.2", "zeroize", ] @@ -1062,6 +1095,25 @@ version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +[[package]] +name = "libdbus-sys" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "linux-keyutils" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "761e49ec5fd8a5a463f9b84e877c373d888935b71c6be78f3767fe2ae6bed18e" +dependencies = [ + "bitflags", + "libc", +] + [[package]] name = "litemap" version = "0.8.1" @@ -1190,6 +1242,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "plist" version = "1.8.0" @@ -1485,7 +1543,7 @@ dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework", + "security-framework 3.5.1", ] [[package]] @@ -1512,7 +1570,7 @@ dependencies = [ "rustls-native-certs", "rustls-platform-verifier-android", "rustls-webpki", - "security-framework", + "security-framework 3.5.1", "security-framework-sys", "webpki-root-certs", "windows-sys 0.61.2", @@ -1559,6 +1617,19 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + [[package]] name = "security-framework" version = "3.5.1" @@ -2416,6 +2487,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.60.2" @@ -2783,6 +2863,20 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "zerotrie" diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index eb40aee..9430ffc 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -5,7 +5,7 @@ use isideload::{ anisette::remote_v3::RemoteV3AnisetteProvider, auth::apple_account::AppleAccount, dev::developer_session::DeveloperSession, - sideload::{SideloadConfiguration, TeamSelection, sideload_app}, + sideload::{SideloaderBuilder, TeamSelection}, }; use tracing::Level; @@ -70,27 +70,26 @@ async fn main() { .unwrap() .to_provider(UsbmuxdAddr::from_env_var().unwrap(), "isideload-demo"); - let sideload_config = - SideloadConfiguration::builder().team_selection(TeamSelection::Prompt(|teams| { - println!("Please select a team:"); - for (index, team) in teams.iter().enumerate() { - println!( - "{}: {} ({})", - index + 1, - team.name.as_deref().unwrap_or(""), - team.team_id - ); - } - let mut input = String::new(); - std::io::stdin().read_line(&mut input).unwrap(); - let selection = input.trim().parse::().ok()?; - if selection == 0 || selection > teams.len() { - return None; - } - Some(teams[selection - 1].team_id.clone()) - })); + let builder = SideloaderBuilder::new().team_selection(TeamSelection::Prompt(|teams| { + println!("Please select a team:"); + for (index, team) in teams.iter().enumerate() { + println!( + "{}: {} ({})", + index + 1, + team.name.as_deref().unwrap_or(""), + team.team_id + ); + } + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + let selection = input.trim().parse::().ok()?; + if selection == 0 || selection > teams.len() { + return None; + } + Some(teams[selection - 1].team_id.clone()) + })); - let result = sideload_app(&provider, &mut dev_session, app_path, &sideload_config).await; + // let result = bu(&provider, &mut dev_session, app_path, &sideload_config).await; match result { Ok(_) => println!("App installed successfully"), Err(e) => panic!("Failed to install app: {:?}", e), diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 9af6964..6e39db5 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -40,4 +40,4 @@ cbc = { version = "0.2.0-rc.3", features = ["alloc"] } aes = "0.9.0-rc.4" aes-gcm = "0.11.0-rc.3" tokio = "1.49.0" -keyring = { version = "3.6.3", optional = true } +keyring = { version = "3.6.3", features = ["apple-native", "linux-native-sync-persistent", "windows-native"], optional = true } diff --git a/isideload/src/sideload/config.rs b/isideload/src/sideload/builder.rs similarity index 68% rename from isideload/src/sideload/config.rs rename to isideload/src/sideload/builder.rs index a09f075..51a8ea1 100644 --- a/isideload/src/sideload/config.rs +++ b/isideload/src/sideload/builder.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use crate::dev::teams::DeveloperTeam; +use crate::{dev::teams::DeveloperTeam, util::storage::SideloadingStorage}; /// Configuration for selecting a developer team during sideloading /// @@ -22,20 +22,22 @@ impl Display for TeamSelection { } } -pub struct SideloadConfiguration { +pub struct SideloaderBuilder { pub team_selection: TeamSelection, + pub storage: Box, } -impl Default for SideloadConfiguration { +impl Default for SideloaderBuilder { fn default() -> Self { - SideloadConfiguration { + SideloaderBuilder { team_selection: TeamSelection::First, + storage: Box::new(crate::util::storage::new_storage()), } } } -impl SideloadConfiguration { - pub fn builder() -> Self { +impl SideloaderBuilder { + pub fn new() -> Self { Self::default() } @@ -43,4 +45,9 @@ impl SideloadConfiguration { self.team_selection = selection; self } + + pub fn storage(mut self, storage: Box) -> Self { + self.storage = storage; + self + } } diff --git a/isideload/src/sideload/mod.rs b/isideload/src/sideload/mod.rs index ecf254f..2446950 100644 --- a/isideload/src/sideload/mod.rs +++ b/isideload/src/sideload/mod.rs @@ -1,51 +1,4 @@ -use std::path::PathBuf; - -use idevice::provider::IdeviceProvider; -use rootcause::prelude::*; -use tracing::info; - -use crate::dev::teams::TeamsApi; -use crate::dev::{developer_session::DeveloperSession, devices::DevicesApi}; -use crate::util::device::IdeviceInfo; - +pub mod builder; pub mod certificate; -pub mod config; -pub use config::{SideloadConfiguration, TeamSelection}; - -pub async fn sideload_app( - device_provider: &impl IdeviceProvider, - dev_session: &mut DeveloperSession, - app_path: PathBuf, - config: &SideloadConfiguration, -) -> Result<(), Report> { - let device_info = IdeviceInfo::from_device(device_provider).await?; - let teams = dev_session.list_teams().await?; - let team = match teams.len() { - 0 => { - bail!("No developer teams available") - } - 1 => &teams[0], - _ => { - info!( - "Multiple developer teams found, {} as per configuration", - config.team_selection - ); - match &config.team_selection { - TeamSelection::First => &teams[0], - TeamSelection::Prompt(prompt_fn) => { - let selection = prompt_fn(&teams).ok_or_else(|| report!("No team selected"))?; - teams - .iter() - .find(|t| t.team_id == selection) - .ok_or_else(|| report!("No team found with ID {}", selection))? - } - } - } - }; - - dev_session - .ensure_device_registered(team, &device_info.name, &device_info.udid, None) - .await?; - - Ok(()) -} +pub mod sideloader; +pub use builder::{SideloaderBuilder, TeamSelection}; diff --git a/isideload/src/sideload/sideloader.rs b/isideload/src/sideload/sideloader.rs new file mode 100644 index 0000000..904ce4a --- /dev/null +++ b/isideload/src/sideload/sideloader.rs @@ -0,0 +1,66 @@ +use crate::{ + dev::{ + developer_session::DeveloperSession, + devices::DevicesApi, + teams::{DeveloperTeam, TeamsApi}, + }, + sideload::TeamSelection, + util::{device::IdeviceInfo, storage::SideloadingStorage}, +}; + +use std::path::PathBuf; + +use idevice::provider::IdeviceProvider; +use rootcause::prelude::*; +use tracing::info; + +pub struct Sideloader { + pub team_selection: TeamSelection, + pub storage: Box, + pub dev_session: DeveloperSession, +} + +impl Sideloader { + pub async fn install_app( + &mut self, + device_provider: &impl IdeviceProvider, + app_path: PathBuf, + ) -> Result<(), Report> { + let device_info = IdeviceInfo::from_device(device_provider).await?; + + let team = self.get_team().await?; + + self.dev_session + .ensure_device_registered(&team, &device_info.name, &device_info.udid, None) + .await?; + + Ok(()) + } + + pub async fn get_team(&mut self) -> Result { + let teams = self.dev_session.list_teams().await?; + Ok(match teams.len() { + 0 => { + bail!("No developer teams available") + } + 1 => teams.into_iter().next().unwrap(), + _ => { + info!( + "Multiple developer teams found, {} as per configuration", + self.team_selection + ); + match &self.team_selection { + TeamSelection::First => teams.into_iter().next().unwrap(), + TeamSelection::Prompt(prompt_fn) => { + let selection = + prompt_fn(&teams).ok_or_else(|| report!("No team selected"))?; + teams + .into_iter() + .find(|t| t.team_id == selection) + .ok_or_else(|| report!("No team found with ID {}", selection))? + } + } + } + }) + } +} diff --git a/isideload/src/util/keyring_storage.rs b/isideload/src/util/keyring_storage.rs index e69de29..bc0f495 100644 --- a/isideload/src/util/keyring_storage.rs +++ b/isideload/src/util/keyring_storage.rs @@ -0,0 +1,36 @@ +use crate::util::storage::SideloadingStorage; +use keyring::Entry; +use rootcause::prelude::*; + +pub struct KeyringStorage {} + +impl KeyringStorage { + pub fn new() -> Self { + KeyringStorage {} + } +} + +impl SideloadingStorage for KeyringStorage { + fn store(&self, key: &str, value: &str) -> Result<(), Report> { + Entry::new("isideload", key)?.set_password(value)?; + Ok(()) + } + + fn retrieve(&self, key: &str) -> Result, Report> { + let entry = Entry::new("isideload", key)?; + match entry.get_password() { + Ok(password) => Ok(Some(password)), + Err(keyring::Error::NoEntry) => Ok(None), + Err(e) => Err(e.into()), + } + } + + fn delete(&self, key: &str) -> Result<(), Report> { + let entry = Entry::new("isideload", key)?; + match entry.delete_credential() { + Ok(()) => Ok(()), + Err(keyring::Error::NoEntry) => Ok(()), + Err(e) => Err(e.into()), + } + } +} diff --git a/isideload/src/util/storage.rs b/isideload/src/util/storage.rs index 109d554..9eccab4 100644 --- a/isideload/src/util/storage.rs +++ b/isideload/src/util/storage.rs @@ -1,9 +1,14 @@ +use std::{collections::HashMap, sync::Mutex}; + use base64::prelude::*; use rootcause::prelude::*; pub trait SideloadingStorage: Send + Sync { fn store(&self, key: &str, value: &str) -> Result<(), Report>; fn retrieve(&self, key: &str) -> Result, Report>; + fn delete(&self, key: &str) -> Result<(), Report> { + self.store(key, "") + } fn store_data(&self, key: &str, value: &[u8]) -> Result<(), Report> { let encoded = BASE64_STANDARD.encode(value); @@ -19,3 +24,45 @@ pub trait SideloadingStorage: Send + Sync { } } } + +pub fn new_storage() -> impl SideloadingStorage { + #[cfg(feature = "keyring-storage")] + { + crate::util::keyring_storage::KeyringStorage::new() + } + #[cfg(not(feature = "keyring-storage"))] + { + InMemoryStorage::new() + } +} + +pub struct InMemoryStorage { + storage: Mutex>, +} + +impl InMemoryStorage { + pub fn new() -> Self { + InMemoryStorage { + storage: Mutex::new(HashMap::new()), + } + } +} + +impl SideloadingStorage for InMemoryStorage { + fn store(&self, key: &str, value: &str) -> Result<(), Report> { + let mut storage = self.storage.lock().unwrap(); + storage.insert(key.to_string(), value.to_string()); + Ok(()) + } + + fn retrieve(&self, key: &str) -> Result, Report> { + let storage = self.storage.lock().unwrap(); + Ok(storage.get(key).cloned()) + } + + fn delete(&self, key: &str) -> Result<(), Report> { + let mut storage = self.storage.lock().unwrap(); + storage.remove(key); + Ok(()) + } +} From a72651163032110c023a9379ee9c50227ab93465 Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 6 Feb 2026 11:13:01 -0500 Subject: [PATCH 45/71] start implementing cert identity --- Cargo.lock | 103 +++++++++++++++++++++++++- examples/minimal/src/main.rs | 42 ++++++----- isideload/Cargo.toml | 4 +- isideload/src/dev/certificates.rs | 27 +++++++ isideload/src/sideload/builder.rs | 32 ++++---- isideload/src/sideload/certificate.rs | 59 ++++++++++++++- isideload/src/sideload/sideloader.rs | 18 ++++- 7 files changed, 245 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 31e7245..4c62fa1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,6 +118,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + [[package]] name = "bitflags" version = "2.10.0" @@ -397,6 +403,30 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der" +version = "0.8.0-rc.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c1d73e9668ea6b6a28172aa55f3ebec38507131ce179051c8033b5c6037653" +dependencies = [ + "const-oid", + "der_derive", + "flagset", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der_derive" +version = "0.8.0-rc.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be645fee2afe89d293b96c19e4456e6ac69520fc9c6b8a58298550138e361ffe" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "deranged" version = "0.5.5" @@ -466,6 +496,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[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.9" @@ -1004,6 +1040,7 @@ dependencies = [ "rootcause", "serde", "serde_json", + "sha1 0.11.0-rc.5", "sha2", "srp", "thiserror 2.0.18", @@ -1011,6 +1048,7 @@ dependencies = [ "tokio-tungstenite", "tracing", "uuid", + "x509-cert", ] [[package]] @@ -1224,6 +1262,15 @@ dependencies = [ "hmac", ] +[[package]] +name = "pem-rfc7468" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -1723,6 +1770,17 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha1" +version = "0.11.0-rc.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b167252f3c126be0d8926639c4c4706950f01445900c4b3db0fd7e89fcb750a" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.11.0-rc.11", +] + [[package]] name = "sha2" version = "0.11.0-rc.5" @@ -1777,6 +1835,16 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "spki" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8baeff88f34ed0691978ec34440140e1572b68c7dd4a495fd14a3dc1944daa80" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "srp" version = "0.7.0-rc.1" @@ -1958,6 +2026,27 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tls_codec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b" +dependencies = [ + "tls_codec_derive", + "zeroize", +] + +[[package]] +name = "tls_codec_derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tokio" version = "1.49.0" @@ -2156,7 +2245,7 @@ dependencies = [ "rand", "rustls", "rustls-pki-types", - "sha1", + "sha1 0.10.6", "thiserror 2.0.18", "utf-8", ] @@ -2794,6 +2883,18 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +[[package]] +name = "x509-cert" +version = "0.3.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e21aad3a769f25f3d2d0cbf30ea8b50a1d602354bd6ab687fad112821608ba6" +dependencies = [ + "const-oid", + "der", + "spki", + "tls_codec", +] + [[package]] name = "yoke" version = "0.8.1" diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 9430ffc..cd33ea5 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -49,7 +49,7 @@ async fn main() { let mut account = account.unwrap(); - let mut dev_session = DeveloperSession::from_account(&mut account) + let dev_session = DeveloperSession::from_account(&mut account) .await .expect("Failed to create developer session"); @@ -70,26 +70,28 @@ async fn main() { .unwrap() .to_provider(UsbmuxdAddr::from_env_var().unwrap(), "isideload-demo"); - let builder = SideloaderBuilder::new().team_selection(TeamSelection::Prompt(|teams| { - println!("Please select a team:"); - for (index, team) in teams.iter().enumerate() { - println!( - "{}: {} ({})", - index + 1, - team.name.as_deref().unwrap_or(""), - team.team_id - ); - } - let mut input = String::new(); - std::io::stdin().read_line(&mut input).unwrap(); - let selection = input.trim().parse::().ok()?; - if selection == 0 || selection > teams.len() { - return None; - } - Some(teams[selection - 1].team_id.clone()) - })); + let mut sideloader = SideloaderBuilder::new(dev_session) + .team_selection(TeamSelection::Prompt(|teams| { + println!("Please select a team:"); + for (index, team) in teams.iter().enumerate() { + println!( + "{}: {} ({})", + index + 1, + team.name.as_deref().unwrap_or(""), + team.team_id + ); + } + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + let selection = input.trim().parse::().ok()?; + if selection == 0 || selection > teams.len() { + return None; + } + Some(teams[selection - 1].team_id.clone()) + })) + .build(); - // let result = bu(&provider, &mut dev_session, app_path, &sideload_config).await; + let result = sideloader.install_app(&provider, app_path).await; match result { Ok(_) => println!("App installed successfully"), Err(e) => panic!("Failed to install app: {:?}", e), diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 6e39db5..42c4d52 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -25,7 +25,6 @@ async-trait = "0.1.89" serde = "1.0.228" rand = "0.9.2" uuid = {version = "1.20.0", features = ["v4"] } -sha2 = "0.11.0-rc.5" tracing = "0.1.44" tokio-tungstenite = { version = "0.28.0", features = ["rustls-tls-webpki-roots"] } rootcause = "0.11.1" @@ -33,6 +32,8 @@ futures-util = "0.3.31" serde_json = "1.0.149" base64 = "0.22.1" hex = "0.4.3" +sha1 = "0.11.0-rc.5" +sha2 = "0.11.0-rc.5" srp = "0.7.0-rc.1" pbkdf2 = "0.13.0-rc.9" hmac = "0.13.0-rc.5" @@ -41,3 +42,4 @@ aes = "0.9.0-rc.4" aes-gcm = "0.11.0-rc.3" tokio = "1.49.0" keyring = { version = "3.6.3", features = ["apple-native", "linux-native-sync-persistent", "windows-native"], optional = true } +x509-cert = "0.3.0-rc.4" diff --git a/isideload/src/dev/certificates.rs b/isideload/src/dev/certificates.rs index 0b6b2e9..cd485a7 100644 --- a/isideload/src/dev/certificates.rs +++ b/isideload/src/dev/certificates.rs @@ -97,6 +97,33 @@ pub trait CertificatesApi { Ok(certs) } + async fn list_ios_certs( + &mut self, + team: &DeveloperTeam, + ) -> Result, Report> { + let certs = self + .list_all_development_certs(team, DeveloperDeviceType::Ios) + .await?; + + Ok(certs + .into_iter() + .filter(|c| { + if let Some(platform) = &c.certificate_platform { + platform.to_lowercase() == "ios" + } else if let Some(cert_type) = &c.certificate_type { + if let Some(platform) = &cert_type.platform { + platform.to_lowercase() == "ios" + } else { + // I don't know how consistently these field is populated because apple apis are stupid, and I don't want to break things so just assume + true + } + } else { + true + } + }) + .collect()) + } + async fn revoke_development_cert( &mut self, team: &DeveloperTeam, diff --git a/isideload/src/sideload/builder.rs b/isideload/src/sideload/builder.rs index 51a8ea1..c7b4972 100644 --- a/isideload/src/sideload/builder.rs +++ b/isideload/src/sideload/builder.rs @@ -1,6 +1,10 @@ use std::fmt::Display; -use crate::{dev::teams::DeveloperTeam, util::storage::SideloadingStorage}; +use crate::{ + dev::{developer_session::DeveloperSession, teams::DeveloperTeam}, + sideload::sideloader::Sideloader, + util::storage::SideloadingStorage, +}; /// Configuration for selecting a developer team during sideloading /// @@ -23,22 +27,18 @@ impl Display for TeamSelection { } pub struct SideloaderBuilder { - pub team_selection: TeamSelection, - pub storage: Box, -} - -impl Default for SideloaderBuilder { - fn default() -> Self { - SideloaderBuilder { - team_selection: TeamSelection::First, - storage: Box::new(crate::util::storage::new_storage()), - } - } + team_selection: TeamSelection, + storage: Box, + developer_session: DeveloperSession, } impl SideloaderBuilder { - pub fn new() -> Self { - Self::default() + pub fn new(developer_session: DeveloperSession) -> Self { + SideloaderBuilder { + team_selection: TeamSelection::First, + storage: Box::new(crate::util::storage::new_storage()), + developer_session, + } } pub fn team_selection(mut self, selection: TeamSelection) -> Self { @@ -50,4 +50,8 @@ impl SideloaderBuilder { self.storage = storage; self } + + pub fn build(self) -> Sideloader { + Sideloader::new(self.team_selection, self.storage, self.developer_session) + } } diff --git a/isideload/src/sideload/certificate.rs b/isideload/src/sideload/certificate.rs index c56d52b..393c631 100644 --- a/isideload/src/sideload/certificate.rs +++ b/isideload/src/sideload/certificate.rs @@ -1,6 +1,63 @@ +use rootcause::prelude::*; +use tracing::{error, info}; + +use crate::{ + dev::{ + certificates::CertificatesApi, developer_session::DeveloperSession, teams::DeveloperTeam, + }, + util::storage::SideloadingStorage, +}; + pub struct CertificateIdentity { pub machine_id: String, pub machine_name: String, } -impl CertificateIdentity {} +impl CertificateIdentity { + pub async fn retrieve( + machine_name: &str, + developer_session: DeveloperSession, + team: &DeveloperTeam, + storage: &dyn SideloadingStorage, + ) -> Result { + let stored = + Self::retrieve_from_storage(machine_name, developer_session, team, storage).await; + if let Ok(Some(cert)) = stored { + return Ok(cert); + } + + if let Err(e) = stored { + error!("Failed to load certificate from storage: {:?}", e); + } else { + info!("No stored certificate found, generating"); + } + + todo!("generate CSR") + } + + async fn retrieve_from_storage( + machine_name: &str, + developer_session: DeveloperSession, + team: &DeveloperTeam, + storage: &dyn SideloadingStorage, + ) -> Result, Report> { + let cert = storage.retrieve_data("cert")?; + if cert.is_none() { + return Ok(None); + } + let cert = cert.unwrap(); + let private_key = storage.retrieve_data("key")?; + if private_key.is_none() { + return Ok(None); + } + + for cert in developer_session + .list_ios_certs(team) + .await? + .iter() + .filter(|c| c.machine_name.unwrap_or_default() == machine_name) + {} + + Ok(()) + } +} diff --git a/isideload/src/sideload/sideloader.rs b/isideload/src/sideload/sideloader.rs index 904ce4a..a91f3fc 100644 --- a/isideload/src/sideload/sideloader.rs +++ b/isideload/src/sideload/sideloader.rs @@ -15,12 +15,24 @@ use rootcause::prelude::*; use tracing::info; pub struct Sideloader { - pub team_selection: TeamSelection, - pub storage: Box, - pub dev_session: DeveloperSession, + team_selection: TeamSelection, + storage: Box, + dev_session: DeveloperSession, } impl Sideloader { + pub fn new( + team_selection: TeamSelection, + storage: Box, + dev_session: DeveloperSession, + ) -> Self { + Sideloader { + team_selection, + storage, + dev_session, + } + } + pub async fn install_app( &mut self, device_provider: &impl IdeviceProvider, From 0509f0025774931d434ffc7643414ca8de087513 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 7 Feb 2026 09:37:44 -0500 Subject: [PATCH 46/71] keep working on cert stuff (broken) --- Cargo.lock | 284 ++++++++++++++++++++------ isideload/Cargo.toml | 5 +- isideload/src/sideload/certificate.rs | 85 ++++++-- isideload/src/util/storage.rs | 14 -- 4 files changed, 300 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4c62fa1..ffdbcbe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,6 +49,45 @@ version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "asn1-rs" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 2.0.18", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-compression" version = "0.4.37" @@ -91,6 +130,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b7b6141e96a8c160799cc2d5adecd5cbbe5054cb8c7c4af53da0f83bb7ad256" dependencies = [ "aws-lc-sys", + "untrusted 0.7.1", "zeroize", ] @@ -335,6 +375,7 @@ dependencies = [ "num-traits", "rand_core 0.10.0", "serdect", + "zeroize", ] [[package]] @@ -358,6 +399,17 @@ dependencies = [ "rand_core 0.10.0", ] +[[package]] +name = "crypto-primes" +version = "0.7.0-pre.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "334a79c97c0b7fa536716dc132fd417d0afbf471440a41fc25a5d9f66d8771cd" +dependencies = [ + "crypto-bigint", + "libm", + "rand_core 0.10.0", +] + [[package]] name = "ctr" version = "0.10.0-rc.3" @@ -410,21 +462,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02c1d73e9668ea6b6a28172aa55f3ebec38507131ce179051c8033b5c6037653" dependencies = [ "const-oid", - "der_derive", - "flagset", "pem-rfc7468", "zeroize", ] [[package]] -name = "der_derive" -version = "0.8.0-rc.6" +name = "der-parser" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be645fee2afe89d293b96c19e4456e6ac69520fc9c6b8a58298550138e361ffe" +checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" dependencies = [ - "proc-macro2", - "quote", - "syn", + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", ] [[package]] @@ -496,12 +549,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" -[[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.9" @@ -1036,11 +1083,12 @@ dependencies = [ "plist", "plist-macro", "rand", + "rcgen", "reqwest", "rootcause", + "rsa", "serde", "serde_json", - "sha1 0.11.0-rc.5", "sha2", "srp", "thiserror 2.0.18", @@ -1048,7 +1096,7 @@ dependencies = [ "tokio-tungstenite", "tracing", "uuid", - "x509-cert", + "x509-parser", ] [[package]] @@ -1142,6 +1190,12 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + [[package]] name = "linux-keyutils" version = "0.2.4" @@ -1195,6 +1249,12 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -1216,6 +1276,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -1225,12 +1295,31 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-conv" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1240,6 +1329,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "oid-registry" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" +dependencies = [ + "asn1-rs", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -1262,6 +1360,16 @@ dependencies = [ "hmac", ] +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64", + "serde_core", +] + [[package]] name = "pem-rfc7468" version = "1.0.0" @@ -1289,6 +1397,26 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "986d2e952779af96ea048f160fd9194e1751b4faea78bcf3ceb456efe008088e" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.11.0-rc.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b226d2cc389763951db8869584fd800cbbe2962bf454e2edeb5172b31ee99774" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.32" @@ -1485,6 +1613,20 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" +[[package]] +name = "rcgen" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10b99e0098aa4082912d4c649628623db6aba77335e4f4569ff5083a6448b32e" +dependencies = [ + "aws-lc-rs", + "pem", + "rustls-pki-types", + "time", + "x509-parser", + "yasna", +] + [[package]] name = "reqwest" version = "0.13.1" @@ -1535,7 +1677,7 @@ dependencies = [ "cfg-if", "getrandom 0.2.17", "libc", - "untrusted", + "untrusted 0.9.0", "windows-sys 0.52.0", ] @@ -1561,12 +1703,39 @@ dependencies = [ "triomphe", ] +[[package]] +name = "rsa" +version = "0.10.0-rc.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b342b99544549f37509ed7fd42b0cea04bfd9ce07c16ca56094cf0fbeefbbcd" +dependencies = [ + "const-oid", + "crypto-bigint", + "crypto-primes", + "digest 0.11.0-rc.11", + "pkcs1", + "pkcs8", + "rand_core 0.10.0", + "signature", + "spki", + "zeroize", +] + [[package]] name = "rustc-hash" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + [[package]] name = "rustls" version = "0.23.36" @@ -1637,7 +1806,7 @@ dependencies = [ "aws-lc-rs", "ring", "rustls-pki-types", - "untrusted", + "untrusted 0.9.0", ] [[package]] @@ -1770,17 +1939,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "sha1" -version = "0.11.0-rc.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b167252f3c126be0d8926639c4c4706950f01445900c4b3db0fd7e89fcb750a" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.11.0-rc.11", -] - [[package]] name = "sha2" version = "0.11.0-rc.5" @@ -1807,6 +1965,16 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signature" +version = "3.0.0-rc.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f1880df446116126965eeec169136b2e0251dba37c6223bcc819569550edea3" +dependencies = [ + "digest 0.11.0-rc.11", + "rand_core 0.10.0", +] + [[package]] name = "simd-adler32" version = "0.3.8" @@ -2026,27 +2194,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "tls_codec" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b" -dependencies = [ - "tls_codec_derive", - "zeroize", -] - -[[package]] -name = "tls_codec_derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tokio" version = "1.49.0" @@ -2245,7 +2392,7 @@ dependencies = [ "rand", "rustls", "rustls-pki-types", - "sha1 0.10.6", + "sha1", "thiserror 2.0.18", "utf-8", ] @@ -2278,6 +2425,12 @@ dependencies = [ "subtle", ] +[[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" @@ -2884,15 +3037,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] -name = "x509-cert" -version = "0.3.0-rc.4" +name = "x509-parser" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e21aad3a769f25f3d2d0cbf30ea8b50a1d602354bd6ab687fad112821608ba6" +checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202" dependencies = [ - "const-oid", - "der", - "spki", - "tls_codec", + "asn1-rs", + "aws-lc-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror 2.0.18", + "time", +] + +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", ] [[package]] diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 42c4d52..798caf2 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -32,7 +32,6 @@ futures-util = "0.3.31" serde_json = "1.0.149" base64 = "0.22.1" hex = "0.4.3" -sha1 = "0.11.0-rc.5" sha2 = "0.11.0-rc.5" srp = "0.7.0-rc.1" pbkdf2 = "0.13.0-rc.9" @@ -40,6 +39,8 @@ hmac = "0.13.0-rc.5" cbc = { version = "0.2.0-rc.3", features = ["alloc"] } aes = "0.9.0-rc.4" aes-gcm = "0.11.0-rc.3" +rsa = { version = "0.10.0-rc.15" } tokio = "1.49.0" keyring = { version = "3.6.3", features = ["apple-native", "linux-native-sync-persistent", "windows-native"], optional = true } -x509-cert = "0.3.0-rc.4" +x509-parser = "0.18.1" +rcgen = { version = "0.14.7", default-features = false, features = ["aws_lc_rs", "pem"] } diff --git a/isideload/src/sideload/certificate.rs b/isideload/src/sideload/certificate.rs index 393c631..06bada3 100644 --- a/isideload/src/sideload/certificate.rs +++ b/isideload/src/sideload/certificate.rs @@ -1,4 +1,10 @@ +use rcgen::{CertificateParams, DistinguishedName, DnType, PKCS_RSA_SHA256}; use rootcause::prelude::*; +use rsa::{ + RsaPrivateKey, + pkcs8::{DecodePrivateKey, EncodePublicKey}, +}; +use sha2::{Digest, Sha256}; use tracing::{error, info}; use crate::{ @@ -16,20 +22,27 @@ pub struct CertificateIdentity { impl CertificateIdentity { pub async fn retrieve( machine_name: &str, - developer_session: DeveloperSession, + apple_email: &str, + developer_session: &mut DeveloperSession, team: &DeveloperTeam, storage: &dyn SideloadingStorage, ) -> Result { - let stored = - Self::retrieve_from_storage(machine_name, developer_session, team, storage).await; + let stored = Self::retrieve_from_storage( + machine_name, + apple_email, + developer_session, + team, + storage, + ) + .await; if let Ok(Some(cert)) = stored { return Ok(cert); } if let Err(e) = stored { - error!("Failed to load certificate from storage: {:?}", e); + error!("Failed to load stored certificate: {:?}", e); } else { - info!("No stored certificate found, generating"); + info!("No stored certificate found"); } todo!("generate CSR") @@ -37,26 +50,70 @@ impl CertificateIdentity { async fn retrieve_from_storage( machine_name: &str, - developer_session: DeveloperSession, + apple_email: &str, + developer_session: &mut DeveloperSession, team: &DeveloperTeam, storage: &dyn SideloadingStorage, ) -> Result, Report> { - let cert = storage.retrieve_data("cert")?; - if cert.is_none() { - return Ok(None); - } - let cert = cert.unwrap(); - let private_key = storage.retrieve_data("key")?; + let mut hasher = Sha256::new(); + hasher.update(apple_email.as_bytes()); + let email_hash = hex::encode(hasher.finalize()); + + let private_key = storage.retrieve(&format!("{}/key", email_hash))?; if private_key.is_none() { return Ok(None); } + let private_key = RsaPrivateKey::from_pkcs8_pem(&private_key.unwrap())?; + let public_key_der = private_key.to_public_key().to_public_key_der()?; for cert in developer_session .list_ios_certs(team) .await? .iter() - .filter(|c| c.machine_name.unwrap_or_default() == machine_name) - {} + .filter(|c| { + c.cert_content.is_some() && c.machine_name.as_deref().unwrap_or("") == machine_name + }) + { + let x509_cert = + x509_parser::parse_x509_certificate(cert.cert_content.as_ref().unwrap().as_ref())?; + if x509_cert + .1 + .tbs_certificate + .subject_pki + .subject_public_key + .data + == public_key_der.as_ref() + { + return Ok(Some(Self { + machine_id: cert + .machine_id + .clone() + .unwrap_or_else(|| x509_cert.1.tbs_certificate.subject.to_string()), + machine_name: machine_name.to_string(), + })); + } + } + + Ok(None) + } + + async fn request_certificate( + machine_name: &str, + developer_session: &mut DeveloperSession, + team: &DeveloperTeam, + ) -> Result { + Ok(()) + } + + fn build_csr(private_key: &RsaPrivateKey) -> Result { + let mut params = CertificateParams::new(vec![])?; + let mut dn = DistinguishedName::new(); + dn.push(DnType::CountryName, "US"); + dn.push(DnType::StateOrProvinceName, "STATE"); + dn.push(DnType::LocalityName, "LOCAL"); + dn.push(DnType::OrganizationName, "ORGNIZATION"); + dn.push(DnType::CommonName, "CN"); + params.distinguished_name = dn; Ok(()) } diff --git a/isideload/src/util/storage.rs b/isideload/src/util/storage.rs index 9eccab4..16f7cfd 100644 --- a/isideload/src/util/storage.rs +++ b/isideload/src/util/storage.rs @@ -9,20 +9,6 @@ pub trait SideloadingStorage: Send + Sync { fn delete(&self, key: &str) -> Result<(), Report> { self.store(key, "") } - - fn store_data(&self, key: &str, value: &[u8]) -> Result<(), Report> { - let encoded = BASE64_STANDARD.encode(value); - self.store(key, &encoded) - } - - fn retrieve_data(&self, key: &str) -> Result>, Report> { - if let Some(encoded) = self.retrieve(key)? { - let decoded = BASE64_STANDARD.decode(&encoded)?; - Ok(Some(decoded)) - } else { - Ok(None) - } - } } pub fn new_storage() -> impl SideloadingStorage { From ec9af89d747820c14f9f83d55d5abbbbe7acf2c9 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 8 Feb 2026 22:43:33 -0500 Subject: [PATCH 47/71] Retrieve certificates --- Cargo.lock | 219 ++++++++++++++++++++-- README.md | 6 + examples/minimal/Cargo.toml | 2 +- examples/minimal/src/main.rs | 10 +- isideload/Cargo.toml | 4 +- isideload/src/anisette/remote_v3/state.rs | 2 +- isideload/src/sideload/builder.rs | 42 ++++- isideload/src/sideload/certificate.rs | 140 +++++++++----- isideload/src/sideload/sideloader.rs | 30 ++- isideload/src/util/storage.rs | 1 - 10 files changed, 373 insertions(+), 83 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ffdbcbe..d339c76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,7 +26,7 @@ checksum = "04097e08a47d9ad181c2e1f4a5fabc9ae06ce8839a333ba9a949bcb0d31fd2a3" dependencies = [ "cipher", "cpubits", - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] @@ -43,6 +43,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" version = "1.0.100" @@ -164,6 +173,16 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[package]] +name = "bcder" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f7c42c9913f68cf9390a225e81ad56a5c515347287eb98baa710090ca1de86d" +dependencies = [ + "bytes", + "smallvec", +] + [[package]] name = "bitflags" version = "2.10.0" @@ -254,6 +273,28 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.0", +] + +[[package]] +name = "chrono" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +dependencies = [ + "iana-time-zone", + "num-traits", + "windows-link", +] + [[package]] name = "cipher" version = "0.5.0" @@ -307,6 +348,12 @@ version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "const-oid" version = "0.10.2" @@ -354,6 +401,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -455,13 +511,23 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid 0.9.6", + "zeroize", +] + [[package]] name = "der" version = "0.8.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02c1d73e9668ea6b6a28172aa55f3ebec38507131ce179051c8033b5c6037653" dependencies = [ - "const-oid", + "const-oid 0.10.2", "pem-rfc7468", "zeroize", ] @@ -506,7 +572,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02b42f1d9edf5207c137646b568a0168ca0ec25b7f9eaf7f9961da51a3d91cea" dependencies = [ "block-buffer 0.11.0", - "const-oid", + "const-oid 0.10.2", "crypto-common 0.2.0", "subtle", ] @@ -901,6 +967,30 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "icu_collections" version = "2.1.1" @@ -1082,7 +1172,7 @@ dependencies = [ "pbkdf2", "plist", "plist-macro", - "rand", + "rand 0.10.0", "rcgen", "reqwest", "rootcause", @@ -1096,7 +1186,7 @@ dependencies = [ "tokio-tungstenite", "tracing", "uuid", - "x509-parser", + "x509-certificate", ] [[package]] @@ -1403,8 +1493,8 @@ version = "0.8.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "986d2e952779af96ea048f160fd9194e1751b4faea78bcf3ceb456efe008088e" dependencies = [ - "der", - "spki", + "der 0.8.0-rc.10", + "spki 0.8.0-rc.4", ] [[package]] @@ -1413,8 +1503,8 @@ version = "0.11.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b226d2cc389763951db8869584fd800cbbe2962bf454e2edeb5172b31ee99774" dependencies = [ - "der", - "spki", + "der 0.8.0-rc.10", + "spki 0.8.0-rc.4", ] [[package]] @@ -1451,7 +1541,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63641a86fddf4b5274f31c43734458ec7acd3133016dbaa37e4e247e1e9acd46" dependencies = [ "cpubits", - "cpufeatures", + "cpufeatures 0.2.17", "universal-hash", ] @@ -1537,7 +1627,7 @@ dependencies = [ "bytes", "getrandom 0.3.4", "lru-slab", - "rand", + "rand 0.9.2", "ring", "rustc-hash", "rustls", @@ -1588,6 +1678,17 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rand" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +dependencies = [ + "chacha20", + "getrandom 0.4.1", + "rand_core 0.10.0", +] + [[package]] name = "rand_chacha" version = "0.9.0" @@ -1598,6 +1699,15 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + [[package]] name = "rand_core" version = "0.9.5" @@ -1709,15 +1819,15 @@ version = "0.10.0-rc.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b342b99544549f37509ed7fd42b0cea04bfd9ce07c16ca56094cf0fbeefbbcd" dependencies = [ - "const-oid", + "const-oid 0.10.2", "crypto-bigint", "crypto-primes", "digest 0.11.0-rc.11", "pkcs1", "pkcs8", "rand_core 0.10.0", - "signature", - "spki", + "signature 3.0.0-rc.10", + "spki 0.8.0-rc.4", "zeroize", ] @@ -1935,7 +2045,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.10.7", ] @@ -1946,7 +2056,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c5f3b1e2dc8aad28310d8410bd4d7e180eca65fca176c52ab00d364475d0024" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.11.0-rc.11", ] @@ -1965,6 +2075,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "signature" version = "3.0.0-rc.10" @@ -2003,6 +2122,16 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der 0.7.10", +] + [[package]] name = "spki" version = "0.8.0-rc.4" @@ -2010,7 +2139,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8baeff88f34ed0691978ec34440140e1572b68c7dd4a495fd14a3dc1944daa80" dependencies = [ "base64ct", - "der", + "der 0.8.0-rc.10", ] [[package]] @@ -2389,7 +2518,7 @@ dependencies = [ "http", "httparse", "log", - "rand", + "rand 0.9.2", "rustls", "rustls-pki-types", "sha1", @@ -2676,6 +2805,41 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-link" version = "0.2.1" @@ -3036,6 +3200,25 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +[[package]] +name = "x509-certificate" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca9eb9a0c822c67129d5b8fcc2806c6bc4f50496b420825069a440669bcfbf7f" +dependencies = [ + "bcder", + "bytes", + "chrono", + "der 0.7.10", + "hex", + "pem", + "ring", + "signature 2.2.0", + "spki 0.7.3", + "thiserror 2.0.18", + "zeroize", +] + [[package]] name = "x509-parser" version = "0.18.1" diff --git a/README.md b/README.md index e5dd7cf..8cf3427 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,12 @@ A Rust library for sideloading iOS applications using an Apple ID. Used in [Cros This branch is home to isideload-next, the next major version of isideload. It features a redesigned API, improved error handling, better entitlement handling, and more. It is not ready! +## TODO + +- [ ] Signing apps +- [ ] Installing apps +- [ ] Remove dependency on ring + ## Licensing This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. diff --git a/examples/minimal/Cargo.toml b/examples/minimal/Cargo.toml index 89f7a4d..d469a2a 100644 --- a/examples/minimal/Cargo.toml +++ b/examples/minimal/Cargo.toml @@ -10,4 +10,4 @@ plist-macro = "0.1.3" tokio = { version = "1.49.0", features = ["rt-multi-thread", "macros"] } tracing = "0.1.44" tracing-subscriber = "0.3.22" -idevice = { version = "0.1.52", features = ["usbmuxd"]} \ No newline at end of file +idevice = { version = "0.1.52", features = ["usbmuxd"] } \ No newline at end of file diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index cd33ea5..ee54c99 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -42,10 +42,10 @@ async fn main() { .login(apple_password, get_2fa_code) .await; - match &account { - Ok(a) => println!("Logged in. {}", a), - Err(e) => panic!("Failed to log in to Apple ID: {:?}", e), - } + // match &account { + // Ok(a) => println!("Logged in. {}", a), + // Err(e) => panic!("Failed to log in to Apple ID: {:?}", e), + // } let mut account = account.unwrap(); @@ -70,7 +70,7 @@ async fn main() { .unwrap() .to_provider(UsbmuxdAddr::from_env_var().unwrap(), "isideload-demo"); - let mut sideloader = SideloaderBuilder::new(dev_session) + let mut sideloader = SideloaderBuilder::new(dev_session, apple_id.to_string()) .team_selection(TeamSelection::Prompt(|teams| { println!("Please select a team:"); for (index, team) in teams.iter().enumerate() { diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 798caf2..0a1f44c 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -23,7 +23,7 @@ reqwest = { version = "0.13.1", features = ["json", "gzip"] } thiserror = "2.0.17" async-trait = "0.1.89" serde = "1.0.228" -rand = "0.9.2" +rand = "0.10.0" uuid = {version = "1.20.0", features = ["v4"] } tracing = "0.1.44" tokio-tungstenite = { version = "0.28.0", features = ["rustls-tls-webpki-roots"] } @@ -42,5 +42,5 @@ aes-gcm = "0.11.0-rc.3" rsa = { version = "0.10.0-rc.15" } tokio = "1.49.0" keyring = { version = "3.6.3", features = ["apple-native", "linux-native-sync-persistent", "windows-native"], optional = true } -x509-parser = "0.18.1" +x509-certificate = "0.25" rcgen = { version = "0.14.7", default-features = false, features = ["aws_lc_rs", "pem"] } diff --git a/isideload/src/anisette/remote_v3/state.rs b/isideload/src/anisette/remote_v3/state.rs index 5be5d5a..a2a1a07 100644 --- a/isideload/src/anisette/remote_v3/state.rs +++ b/isideload/src/anisette/remote_v3/state.rs @@ -1,7 +1,7 @@ // Serialization/Desieralization borrowed from https://github.com/SideStore/apple-private-apis/blob/master/omnisette/src/remote_anisette_v3.rs use plist::Data; -use rand::Rng; +use rand::RngExt; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use sha2::{Digest, Sha256}; use uuid::Uuid; diff --git a/isideload/src/sideload/builder.rs b/isideload/src/sideload/builder.rs index c7b4972..c305767 100644 --- a/isideload/src/sideload/builder.rs +++ b/isideload/src/sideload/builder.rs @@ -26,32 +26,58 @@ impl Display for TeamSelection { } } +pub enum MaxCertsBehavior { + /// If the maximum number of certificates is reached, delete all existing certificates and create a new one + Revoke, + /// If the maximum number of certificates is reached, return an error instead of creating a new certificate + Error, +} + pub struct SideloaderBuilder { - team_selection: TeamSelection, - storage: Box, developer_session: DeveloperSession, + apple_email: String, + team_selection: Option, + max_certs_behavior: Option, + storage: Option>, + machine_name: Option, } impl SideloaderBuilder { - pub fn new(developer_session: DeveloperSession) -> Self { + pub fn new(developer_session: DeveloperSession, apple_email: String) -> Self { SideloaderBuilder { - team_selection: TeamSelection::First, - storage: Box::new(crate::util::storage::new_storage()), + team_selection: None, + storage: None, developer_session, + machine_name: None, + apple_email, + max_certs_behavior: None, } } pub fn team_selection(mut self, selection: TeamSelection) -> Self { - self.team_selection = selection; + self.team_selection = Some(selection); self } pub fn storage(mut self, storage: Box) -> Self { - self.storage = storage; + self.storage = Some(storage); + self + } + + pub fn machine_name(mut self, machine_name: String) -> Self { + self.machine_name = Some(machine_name); self } pub fn build(self) -> Sideloader { - Sideloader::new(self.team_selection, self.storage, self.developer_session) + Sideloader::new( + self.developer_session, + self.apple_email, + self.team_selection.unwrap_or(TeamSelection::First), + self.max_certs_behavior.unwrap_or(MaxCertsBehavior::Revoke), + self.machine_name.unwrap_or_else(|| "isideload".to_string()), + self.storage + .unwrap_or_else(|| Box::new(crate::util::storage::new_storage())), + ) } } diff --git a/isideload/src/sideload/certificate.rs b/isideload/src/sideload/certificate.rs index 06bada3..be21d2f 100644 --- a/isideload/src/sideload/certificate.rs +++ b/isideload/src/sideload/certificate.rs @@ -1,22 +1,27 @@ -use rcgen::{CertificateParams, DistinguishedName, DnType, PKCS_RSA_SHA256}; +use rcgen::{CertificateParams, DistinguishedName, DnType, KeyPair, PKCS_RSA_SHA256}; use rootcause::prelude::*; use rsa::{ RsaPrivateKey, - pkcs8::{DecodePrivateKey, EncodePublicKey}, + pkcs1::EncodeRsaPublicKey, + pkcs8::{DecodePrivateKey, EncodePrivateKey, LineEnding}, }; + use sha2::{Digest, Sha256}; use tracing::{error, info}; +use x509_certificate::X509Certificate; use crate::{ dev::{ certificates::CertificatesApi, developer_session::DeveloperSession, teams::DeveloperTeam, }, + sideload::builder::MaxCertsBehavior, util::storage::SideloadingStorage, }; pub struct CertificateIdentity { pub machine_id: String, pub machine_name: String, + pub certificate: X509Certificate, } impl CertificateIdentity { @@ -26,70 +31,82 @@ impl CertificateIdentity { developer_session: &mut DeveloperSession, team: &DeveloperTeam, storage: &dyn SideloadingStorage, + max_certs_behavior: &MaxCertsBehavior, ) -> Result { - let stored = Self::retrieve_from_storage( - machine_name, - apple_email, - developer_session, - team, - storage, - ) - .await; - if let Ok(Some(cert)) = stored { + let pr = Self::retrieve_private_key(apple_email, storage).await?; + + let found = Self::find_matching(&pr, machine_name, developer_session, team).await; + if let Ok(Some(cert)) = found { + info!("Found matching certificate"); return Ok(cert); } - if let Err(e) = stored { - error!("Failed to load stored certificate: {:?}", e); - } else { - info!("No stored certificate found"); + if let Err(e) = found { + error!("Failed to check for matching certificate: {:?}", e); } - - todo!("generate CSR") + info!("Requesting new certificate"); + Self::request_certificate( + &pr, + machine_name.to_string(), + developer_session, + team, + max_certs_behavior, + ) + .await } - async fn retrieve_from_storage( - machine_name: &str, + async fn retrieve_private_key( apple_email: &str, - developer_session: &mut DeveloperSession, - team: &DeveloperTeam, storage: &dyn SideloadingStorage, - ) -> Result, Report> { + ) -> Result { let mut hasher = Sha256::new(); hasher.update(apple_email.as_bytes()); let email_hash = hex::encode(hasher.finalize()); let private_key = storage.retrieve(&format!("{}/key", email_hash))?; - if private_key.is_none() { - return Ok(None); + if private_key.is_some() { + return Ok(RsaPrivateKey::from_pkcs8_pem(&private_key.unwrap())?); } - let private_key = RsaPrivateKey::from_pkcs8_pem(&private_key.unwrap())?; - let public_key_der = private_key.to_public_key().to_public_key_der()?; + let mut rng = rand::rng(); + let private_key = RsaPrivateKey::new(&mut rng, 2048)?; + storage.store( + &format!("{}/key", email_hash), + &private_key.to_pkcs8_pem(Default::default())?.to_string(), + )?; + + Ok(private_key) + } + + async fn find_matching( + private_key: &RsaPrivateKey, + machine_name: &str, + developer_session: &mut DeveloperSession, + team: &DeveloperTeam, + ) -> Result, Report> { + let public_key_der = private_key + .to_public_key() + .to_pkcs1_der()? + .as_bytes() + .to_vec(); for cert in developer_session .list_ios_certs(team) .await? .iter() .filter(|c| { - c.cert_content.is_some() && c.machine_name.as_deref().unwrap_or("") == machine_name + c.cert_content.is_some() + && c.machine_name.as_deref().unwrap_or("") == machine_name + && c.machine_id.is_some() }) { let x509_cert = - x509_parser::parse_x509_certificate(cert.cert_content.as_ref().unwrap().as_ref())?; - if x509_cert - .1 - .tbs_certificate - .subject_pki - .subject_public_key - .data - == public_key_der.as_ref() - { + X509Certificate::from_der(cert.cert_content.as_ref().unwrap().as_ref())?; + + if public_key_der == x509_cert.public_key_data().as_ref() { return Ok(Some(Self { - machine_id: cert - .machine_id - .clone() - .unwrap_or_else(|| x509_cert.1.tbs_certificate.subject.to_string()), - machine_name: machine_name.to_string(), + machine_id: cert.machine_id.clone().unwrap_or_default(), + machine_name: cert.machine_name.clone().unwrap_or_default(), + certificate: x509_cert, })); } } @@ -98,16 +115,46 @@ impl CertificateIdentity { } async fn request_certificate( - machine_name: &str, + private_key: &RsaPrivateKey, + machine_name: String, developer_session: &mut DeveloperSession, team: &DeveloperTeam, + max_certs_behavior: &MaxCertsBehavior, ) -> Result { - Ok(()) + let csr = Self::build_csr(private_key).context("Failed to generate CSR")?; + + let request = developer_session + .submit_development_csr(team, csr, machine_name, None) + .await?; + + // TODO: Handle max certs behavior properly instead of just always revoking + + let apple_certs = developer_session.list_ios_certs(team).await?; + + let apple_cert = apple_certs + .iter() + .find(|c| c.certificate_id == Some(request.cert_request_id.clone())) + .ok_or_else(|| report!("Failed to find certificate after submitting CSR"))?; + + let x509_cert = X509Certificate::from_der( + apple_cert + .cert_content + .as_ref() + .ok_or_else(|| report!("Certificate content missing"))? + .as_ref(), + )?; + + Ok(Self { + machine_id: apple_cert.machine_id.clone().unwrap_or_default(), + machine_name: apple_cert.machine_name.clone().unwrap_or_default(), + certificate: x509_cert, + }) } fn build_csr(private_key: &RsaPrivateKey) -> Result { let mut params = CertificateParams::new(vec![])?; let mut dn = DistinguishedName::new(); + dn.push(DnType::CountryName, "US"); dn.push(DnType::StateOrProvinceName, "STATE"); dn.push(DnType::LocalityName, "LOCAL"); @@ -115,6 +162,11 @@ impl CertificateIdentity { dn.push(DnType::CommonName, "CN"); params.distinguished_name = dn; - Ok(()) + let subject_key = KeyPair::from_pkcs8_pem_and_sign_algo( + &private_key.to_pkcs8_pem(LineEnding::LF)?, + &PKCS_RSA_SHA256, + )?; + + Ok(params.serialize_request(&subject_key)?.pem()?) } } diff --git a/isideload/src/sideload/sideloader.rs b/isideload/src/sideload/sideloader.rs index a91f3fc..39bbaed 100644 --- a/isideload/src/sideload/sideloader.rs +++ b/isideload/src/sideload/sideloader.rs @@ -4,7 +4,7 @@ use crate::{ devices::DevicesApi, teams::{DeveloperTeam, TeamsApi}, }, - sideload::TeamSelection, + sideload::{TeamSelection, builder::MaxCertsBehavior, certificate::CertificateIdentity}, util::{device::IdeviceInfo, storage::SideloadingStorage}, }; @@ -18,18 +18,27 @@ pub struct Sideloader { team_selection: TeamSelection, storage: Box, dev_session: DeveloperSession, + machine_name: String, + apple_email: String, + max_certs_behavior: MaxCertsBehavior, } impl Sideloader { pub fn new( - team_selection: TeamSelection, - storage: Box, dev_session: DeveloperSession, + apple_email: String, + team_selection: TeamSelection, + max_certs_behavior: MaxCertsBehavior, + machine_name: String, + storage: Box, ) -> Self { Sideloader { team_selection, storage, dev_session, + machine_name, + apple_email, + max_certs_behavior, } } @@ -46,6 +55,21 @@ impl Sideloader { .ensure_device_registered(&team, &device_info.name, &device_info.udid, None) .await?; + let cert_identity = CertificateIdentity::retrieve( + &self.machine_name, + &self.apple_email, + &mut self.dev_session, + &team, + self.storage.as_ref(), + &self.max_certs_behavior, + ) + .await?; + + // info!( + // "Using certificate for machine {} with ID {}", + // cert_identity.machine_name, cert_identity.machine_id + // ); + Ok(()) } diff --git a/isideload/src/util/storage.rs b/isideload/src/util/storage.rs index 16f7cfd..27fbb03 100644 --- a/isideload/src/util/storage.rs +++ b/isideload/src/util/storage.rs @@ -1,6 +1,5 @@ use std::{collections::HashMap, sync::Mutex}; -use base64::prelude::*; use rootcause::prelude::*; pub trait SideloadingStorage: Send + Sync { From 78eb7fdfccff7282d8127bb67676ea8d20f11607 Mon Sep 17 00:00:00 2001 From: nab138 Date: Mon, 9 Feb 2026 09:24:44 -0500 Subject: [PATCH 48/71] Implement cert revoke and prompt modes --- Cargo.lock | 8 +- examples/minimal/src/main.rs | 76 ++++++++--- isideload/Cargo.toml | 2 +- isideload/src/auth/apple_account.rs | 1 - isideload/src/sideload/builder.rs | 16 ++- isideload/src/sideload/certificate.rs | 177 ++++++++++++++++++++------ isideload/src/util/keyring_storage.rs | 36 +++++- isideload/src/util/storage.rs | 16 ++- 8 files changed, 259 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d339c76..689420d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1793,9 +1793,9 @@ dependencies = [ [[package]] name = "rootcause" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a751633dcb95a6b1c954f0fa15c2afd9b4802640f8045432f68a1f4bde4b871" +checksum = "03621279b1bafd0cd806d4a4e301530bfab4a54a9a572ea45a4fe5072c3e134b" dependencies = [ "hashbrown 0.16.1", "indexmap", @@ -1806,9 +1806,9 @@ dependencies = [ [[package]] name = "rootcause-internals" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9eeddca0d656f1a58ce3fc3f41b0b877a7e760460108712ad39b60181fdcb3e" +checksum = "2a6575ad7db4a6f026820de38c377b3e06fc59ceac225f868dfede39cd70e432" dependencies = [ "triomphe", ] diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index ee54c99..0ab8b1e 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -4,8 +4,11 @@ use idevice::usbmuxd::{UsbmuxdAddr, UsbmuxdConnection}; use isideload::{ anisette::remote_v3::RemoteV3AnisetteProvider, auth::apple_account::AppleAccount, - dev::developer_session::DeveloperSession, - sideload::{SideloaderBuilder, TeamSelection}, + dev::{ + certificates::DevelopmentCertificate, developer_session::DeveloperSession, + teams::DeveloperTeam, + }, + sideload::{SideloaderBuilder, TeamSelection, builder::MaxCertsBehavior}, }; use tracing::Level; @@ -70,25 +73,58 @@ async fn main() { .unwrap() .to_provider(UsbmuxdAddr::from_env_var().unwrap(), "isideload-demo"); + let team_selection_prompt = |teams: &Vec| { + println!("Please select a team:"); + for (index, team) in teams.iter().enumerate() { + println!( + "{}: {} ({})", + index + 1, + team.name.as_deref().unwrap_or(""), + team.team_id + ); + } + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + let selection = input.trim().parse::().ok()?; + if selection == 0 || selection > teams.len() { + return None; + } + Some(teams[selection - 1].team_id.clone()) + }; + + let cert_selection_prompt = |certs: &Vec| { + println!("Maximum number of certificates reached. Please select certificates to revoke:"); + for (index, cert) in certs.iter().enumerate() { + println!( + "({}) {}: {}", + index + 1, + cert.name.as_deref().unwrap_or(""), + cert.machine_name.as_deref().unwrap_or(""), + ); + } + println!("Enter the numbers of the certificates to revoke, separated by commas:"); + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + let selections: Vec = input + .trim() + .split(',') + .filter_map(|s| s.trim().parse::().ok()) + .filter(|&n| n > 0 && n <= certs.len()) + .collect(); + if selections.is_empty() { + return None; + } + Some( + selections + .into_iter() + .map(|n| certs[n - 1].clone()) + .collect::>(), + ) + }; + let mut sideloader = SideloaderBuilder::new(dev_session, apple_id.to_string()) - .team_selection(TeamSelection::Prompt(|teams| { - println!("Please select a team:"); - for (index, team) in teams.iter().enumerate() { - println!( - "{}: {} ({})", - index + 1, - team.name.as_deref().unwrap_or(""), - team.team_id - ); - } - let mut input = String::new(); - std::io::stdin().read_line(&mut input).unwrap(); - let selection = input.trim().parse::().ok()?; - if selection == 0 || selection > teams.len() { - return None; - } - Some(teams[selection - 1].team_id.clone()) - })) + .team_selection(TeamSelection::Prompt(team_selection_prompt)) + .max_certs_behavior(MaxCertsBehavior::Prompt(cert_selection_prompt)) .build(); let result = sideloader.install_app(&provider, app_path).await; diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 0a1f44c..d36264c 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -27,7 +27,7 @@ rand = "0.10.0" uuid = {version = "1.20.0", features = ["v4"] } tracing = "0.1.44" tokio-tungstenite = { version = "0.28.0", features = ["rustls-tls-webpki-roots"] } -rootcause = "0.11.1" +rootcause = "0.12.0" futures-util = "0.3.31" serde_json = "1.0.149" base64 = "0.22.1" diff --git a/isideload/src/auth/apple_account.rs b/isideload/src/auth/apple_account.rs index 2524cb8..13f771b 100644 --- a/isideload/src/auth/apple_account.rs +++ b/isideload/src/auth/apple_account.rs @@ -63,7 +63,6 @@ impl AppleAccount { anisette_generator: AnisetteDataGenerator, debug: bool, ) -> Result { - info!("Initializing apple account"); if debug { warn!("Debug mode enabled: this is a security risk!"); } diff --git a/isideload/src/sideload/builder.rs b/isideload/src/sideload/builder.rs index c305767..0f243ea 100644 --- a/isideload/src/sideload/builder.rs +++ b/isideload/src/sideload/builder.rs @@ -1,7 +1,10 @@ use std::fmt::Display; use crate::{ - dev::{developer_session::DeveloperSession, teams::DeveloperTeam}, + dev::{ + certificates::DevelopmentCertificate, developer_session::DeveloperSession, + teams::DeveloperTeam, + }, sideload::sideloader::Sideloader, util::storage::SideloadingStorage, }; @@ -27,10 +30,12 @@ impl Display for TeamSelection { } pub enum MaxCertsBehavior { - /// If the maximum number of certificates is reached, delete all existing certificates and create a new one + /// If the maximum number of certificates is reached, revoke certs until it is possible to create a new certificate Revoke, /// If the maximum number of certificates is reached, return an error instead of creating a new certificate Error, + /// If the maximum number of certificates is reached, prompt the user to select which certificates to revoke until it is possible to create a new certificate + Prompt(fn(&Vec) -> Option>), } pub struct SideloaderBuilder { @@ -69,12 +74,17 @@ impl SideloaderBuilder { self } + pub fn max_certs_behavior(mut self, behavior: MaxCertsBehavior) -> Self { + self.max_certs_behavior = Some(behavior); + self + } + pub fn build(self) -> Sideloader { Sideloader::new( self.developer_session, self.apple_email, self.team_selection.unwrap_or(TeamSelection::First), - self.max_certs_behavior.unwrap_or(MaxCertsBehavior::Revoke), + self.max_certs_behavior.unwrap_or(MaxCertsBehavior::Error), self.machine_name.unwrap_or_else(|| "isideload".to_string()), self.storage .unwrap_or_else(|| Box::new(crate::util::storage::new_storage())), diff --git a/isideload/src/sideload/certificate.rs b/isideload/src/sideload/certificate.rs index be21d2f..b8b511c 100644 --- a/isideload/src/sideload/certificate.rs +++ b/isideload/src/sideload/certificate.rs @@ -11,8 +11,11 @@ use tracing::{error, info}; use x509_certificate::X509Certificate; use crate::{ + SideloadError, dev::{ - certificates::CertificatesApi, developer_session::DeveloperSession, teams::DeveloperTeam, + certificates::{CertificatesApi, DevelopmentCertificate}, + developer_session::DeveloperSession, + teams::DeveloperTeam, }, sideload::builder::MaxCertsBehavior, util::storage::SideloadingStorage, @@ -22,6 +25,7 @@ pub struct CertificateIdentity { pub machine_id: String, pub machine_name: String, pub certificate: X509Certificate, + pub private_key: RsaPrivateKey, } impl CertificateIdentity { @@ -36,23 +40,37 @@ impl CertificateIdentity { let pr = Self::retrieve_private_key(apple_email, storage).await?; let found = Self::find_matching(&pr, machine_name, developer_session, team).await; - if let Ok(Some(cert)) = found { + if let Ok(Some((cert, x509_cert))) = found { info!("Found matching certificate"); - return Ok(cert); + return Ok(Self { + machine_id: cert.machine_id.clone().unwrap_or_default(), + machine_name: cert.machine_name.clone().unwrap_or_default(), + certificate: x509_cert, + private_key: pr, + }); } if let Err(e) = found { error!("Failed to check for matching certificate: {:?}", e); } info!("Requesting new certificate"); - Self::request_certificate( + let (cert, x509_cert) = Self::request_certificate( &pr, machine_name.to_string(), developer_session, team, max_certs_behavior, ) - .await + .await?; + + info!("Successfully obtained certificate"); + + Ok(Self { + machine_id: cert.machine_id.clone().unwrap_or_default(), + machine_name: cert.machine_name.clone().unwrap_or_default(), + certificate: x509_cert, + private_key: pr, + }) } async fn retrieve_private_key( @@ -63,16 +81,16 @@ impl CertificateIdentity { hasher.update(apple_email.as_bytes()); let email_hash = hex::encode(hasher.finalize()); - let private_key = storage.retrieve(&format!("{}/key", email_hash))?; + let private_key = storage.retrieve_data(&format!("{}/key", email_hash))?; if private_key.is_some() { - return Ok(RsaPrivateKey::from_pkcs8_pem(&private_key.unwrap())?); + return Ok(RsaPrivateKey::from_pkcs8_der(&private_key.unwrap())?); } let mut rng = rand::rng(); let private_key = RsaPrivateKey::new(&mut rng, 2048)?; - storage.store( + storage.store_data( &format!("{}/key", email_hash), - &private_key.to_pkcs8_pem(Default::default())?.to_string(), + &private_key.to_pkcs8_der()?.as_bytes(), )?; Ok(private_key) @@ -83,7 +101,7 @@ impl CertificateIdentity { machine_name: &str, developer_session: &mut DeveloperSession, team: &DeveloperTeam, - ) -> Result, Report> { + ) -> Result, Report> { let public_key_der = private_key .to_public_key() .to_pkcs1_der()? @@ -103,11 +121,7 @@ impl CertificateIdentity { X509Certificate::from_der(cert.cert_content.as_ref().unwrap().as_ref())?; if public_key_der == x509_cert.public_key_data().as_ref() { - return Ok(Some(Self { - machine_id: cert.machine_id.clone().unwrap_or_default(), - machine_name: cert.machine_name.clone().unwrap_or_default(), - certificate: x509_cert, - })); + return Ok(Some((cert.clone(), x509_cert))); } } @@ -120,35 +134,77 @@ impl CertificateIdentity { developer_session: &mut DeveloperSession, team: &DeveloperTeam, max_certs_behavior: &MaxCertsBehavior, - ) -> Result { + ) -> Result<(DevelopmentCertificate, X509Certificate), Report> { let csr = Self::build_csr(private_key).context("Failed to generate CSR")?; - let request = developer_session - .submit_development_csr(team, csr, machine_name, None) - .await?; + let mut i = 0; + let mut existing_certs: Option> = None; - // TODO: Handle max certs behavior properly instead of just always revoking + while i < 4 { + i += 1; - let apple_certs = developer_session.list_ios_certs(team).await?; + let result = developer_session + .submit_development_csr(team, csr.clone(), machine_name.clone(), None) + .await; - let apple_cert = apple_certs - .iter() - .find(|c| c.certificate_id == Some(request.cert_request_id.clone())) - .ok_or_else(|| report!("Failed to find certificate after submitting CSR"))?; + match result { + Ok(request) => { + let apple_certs = developer_session.list_ios_certs(team).await?; - let x509_cert = X509Certificate::from_der( - apple_cert - .cert_content - .as_ref() - .ok_or_else(|| report!("Certificate content missing"))? - .as_ref(), - )?; + let apple_cert = apple_certs + .iter() + .find(|c| c.certificate_id == Some(request.cert_request_id.clone())) + .ok_or_else(|| { + report!("Failed to find certificate after submitting CSR") + })?; - Ok(Self { - machine_id: apple_cert.machine_id.clone().unwrap_or_default(), - machine_name: apple_cert.machine_name.clone().unwrap_or_default(), - certificate: x509_cert, - }) + let x509_cert = X509Certificate::from_der( + apple_cert + .cert_content + .as_ref() + .ok_or_else(|| report!("Certificate content missing"))? + .as_ref(), + )?; + + return Ok((apple_cert.clone(), x509_cert)); + } + Err(e) => { + let error = e + .iter_reports() + .find_map(|node| node.downcast_current_context::()); + if let Some(SideloadError::DeveloperError(code, _)) = error { + if *code == 7460 { + if existing_certs.is_none() { + existing_certs = Some( + developer_session + .list_ios_certs(team) + .await? + .iter() + .filter(|c| c.serial_number.is_some()) + .cloned() + .collect(), + ); + } + Self::revoke_others( + developer_session, + team, + max_certs_behavior, + SideloadError::DeveloperError( + *code, + "Maximum number of certificates reached".to_string(), + ), + &mut existing_certs.as_mut().unwrap(), + ) + .await?; + } else { + return Err(e); + } + } + } + }; + } + + Err(report!("Reached max attempts to request certificate")) } fn build_csr(private_key: &RsaPrivateKey) -> Result { @@ -169,4 +225,51 @@ impl CertificateIdentity { Ok(params.serialize_request(&subject_key)?.pem()?) } + + async fn revoke_others( + developer_session: &mut DeveloperSession, + team: &DeveloperTeam, + max_certs_behavior: &MaxCertsBehavior, + error: SideloadError, + existing_certs: &mut Vec, + ) -> Result<(), Report> { + match max_certs_behavior { + MaxCertsBehavior::Revoke => { + if let Some(cert) = existing_certs.pop() { + info!( + "Revoking certificate with name: {:?} ({:?})", + cert.name, cert.machine_name + ); + developer_session + .revoke_development_cert(team, &cert.serial_number.unwrap(), None) + .await?; + Ok(()) + } else { + error!("No more certificates to revoke but still hitting max certs error"); + return Err(error.into()); + } + } + MaxCertsBehavior::Error => Err(error.into()), + MaxCertsBehavior::Prompt(prompt_fn) => { + let certs_to_revoke = prompt_fn(existing_certs); + if certs_to_revoke.is_none() { + error!("User did not select any certificates to revoke"); + return Err(error.into()); + } + for cert in certs_to_revoke.unwrap() { + info!( + "Revoking certificate with name: {}", + cert.machine_name + .unwrap_or(cert.machine_id.unwrap_or_default()) + ); + let serial_number = cert.serial_number.clone(); + developer_session + .revoke_development_cert(team, &cert.serial_number.unwrap(), None) + .await?; + existing_certs.retain(|c| c.serial_number != serial_number); + } + Ok(()) + } + } + } } diff --git a/isideload/src/util/keyring_storage.rs b/isideload/src/util/keyring_storage.rs index bc0f495..7ff4716 100644 --- a/isideload/src/util/keyring_storage.rs +++ b/isideload/src/util/keyring_storage.rs @@ -2,22 +2,32 @@ use crate::util::storage::SideloadingStorage; use keyring::Entry; use rootcause::prelude::*; -pub struct KeyringStorage {} +pub struct KeyringStorage { + pub service_name: String, +} impl KeyringStorage { - pub fn new() -> Self { - KeyringStorage {} + pub fn new(service_name: String) -> Self { + KeyringStorage { service_name } + } +} + +impl Default for KeyringStorage { + fn default() -> Self { + KeyringStorage { + service_name: "isideload".to_string(), + } } } impl SideloadingStorage for KeyringStorage { fn store(&self, key: &str, value: &str) -> Result<(), Report> { - Entry::new("isideload", key)?.set_password(value)?; + Entry::new(&self.service_name, key)?.set_password(value)?; Ok(()) } fn retrieve(&self, key: &str) -> Result, Report> { - let entry = Entry::new("isideload", key)?; + let entry = Entry::new(&self.service_name, key)?; match entry.get_password() { Ok(password) => Ok(Some(password)), Err(keyring::Error::NoEntry) => Ok(None), @@ -26,11 +36,25 @@ impl SideloadingStorage for KeyringStorage { } fn delete(&self, key: &str) -> Result<(), Report> { - let entry = Entry::new("isideload", key)?; + let entry = Entry::new(&self.service_name, key)?; match entry.delete_credential() { Ok(()) => Ok(()), Err(keyring::Error::NoEntry) => Ok(()), Err(e) => Err(e.into()), } } + + fn store_data(&self, key: &str, value: &[u8]) -> Result<(), Report> { + Entry::new(&self.service_name, key)?.set_secret(value)?; + Ok(()) + } + + fn retrieve_data(&self, key: &str) -> Result>, Report> { + let entry = Entry::new(&self.service_name, key)?; + match entry.get_secret() { + Ok(secret) => Ok(Some(secret)), + Err(keyring::Error::NoEntry) => Ok(None), + Err(e) => Err(e.into()), + } + } } diff --git a/isideload/src/util/storage.rs b/isideload/src/util/storage.rs index 27fbb03..0480174 100644 --- a/isideload/src/util/storage.rs +++ b/isideload/src/util/storage.rs @@ -1,10 +1,24 @@ use std::{collections::HashMap, sync::Mutex}; +use base64::prelude::*; use rootcause::prelude::*; pub trait SideloadingStorage: Send + Sync { fn store(&self, key: &str, value: &str) -> Result<(), Report>; fn retrieve(&self, key: &str) -> Result, Report>; + + fn store_data(&self, key: &str, value: &[u8]) -> Result<(), Report> { + self.store(key, &BASE64_STANDARD.encode(value)) + } + + fn retrieve_data(&self, key: &str) -> Result>, Report> { + if let Some(value) = self.retrieve(key)? { + Ok(Some(BASE64_STANDARD.decode(value)?)) + } else { + Ok(None) + } + } + fn delete(&self, key: &str) -> Result<(), Report> { self.store(key, "") } @@ -13,7 +27,7 @@ pub trait SideloadingStorage: Send + Sync { pub fn new_storage() -> impl SideloadingStorage { #[cfg(feature = "keyring-storage")] { - crate::util::keyring_storage::KeyringStorage::new() + crate::util::keyring_storage::KeyringStorage::default() } #[cfg(not(feature = "keyring-storage"))] { From 3f256be37117ca6116f7d731cb874599bab54a5f Mon Sep 17 00:00:00 2001 From: nab138 Date: Mon, 9 Feb 2026 09:26:00 -0500 Subject: [PATCH 49/71] add custom machine name into example --- examples/minimal/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 0ab8b1e..0f326fa 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -125,6 +125,7 @@ async fn main() { let mut sideloader = SideloaderBuilder::new(dev_session, apple_id.to_string()) .team_selection(TeamSelection::Prompt(team_selection_prompt)) .max_certs_behavior(MaxCertsBehavior::Prompt(cert_selection_prompt)) + .machine_name("isideload-demo".to_string()) .build(); let result = sideloader.install_app(&provider, app_path).await; From 1983e9dab7779507e52ede00d7e7683344955150 Mon Sep 17 00:00:00 2001 From: nab138 Date: Mon, 9 Feb 2026 09:33:48 -0500 Subject: [PATCH 50/71] Add docs --- isideload/src/sideload/builder.rs | 16 ++++++++++++++++ .../{certificate.rs => cert_identity.rs} | 0 isideload/src/sideload/mod.rs | 2 +- isideload/src/sideload/sideloader.rs | 7 ++++++- 4 files changed, 23 insertions(+), 2 deletions(-) rename isideload/src/sideload/{certificate.rs => cert_identity.rs} (100%) diff --git a/isideload/src/sideload/builder.rs b/isideload/src/sideload/builder.rs index 0f243ea..776e218 100644 --- a/isideload/src/sideload/builder.rs +++ b/isideload/src/sideload/builder.rs @@ -29,6 +29,7 @@ impl Display for TeamSelection { } } +/// Behavior when the maximum number of development certificates is reached pub enum MaxCertsBehavior { /// If the maximum number of certificates is reached, revoke certs until it is possible to create a new certificate Revoke, @@ -59,26 +60,41 @@ impl SideloaderBuilder { } } + /// Set the team selection behavior + /// + /// See [`TeamSelection`] for details. pub fn team_selection(mut self, selection: TeamSelection) -> Self { self.team_selection = Some(selection); self } + /// Set the storage backend for sideloading data + /// + /// An implementation using `keyring` is provided in the `keyring-storage` feature. + /// See [`SideloadingStorage`] for details. + /// + /// If not set, either keyring storage or in memory storage (not persisted across runs) will be used depending on if the `keyring-storage` feature is enabled. pub fn storage(mut self, storage: Box) -> Self { self.storage = Some(storage); self } + /// Set the machine name to use for the development certificate + /// + /// This has no bearing on functionality but can be useful for users to identify where a certificate came from. + /// If not set, a default name of "isideload" will be used. pub fn machine_name(mut self, machine_name: String) -> Self { self.machine_name = Some(machine_name); self } + /// Set the behavior for when the maximum number of development certificates is reached pub fn max_certs_behavior(mut self, behavior: MaxCertsBehavior) -> Self { self.max_certs_behavior = Some(behavior); self } + /// Build the `Sideloader` instance with the provided configuration pub fn build(self) -> Sideloader { Sideloader::new( self.developer_session, diff --git a/isideload/src/sideload/certificate.rs b/isideload/src/sideload/cert_identity.rs similarity index 100% rename from isideload/src/sideload/certificate.rs rename to isideload/src/sideload/cert_identity.rs diff --git a/isideload/src/sideload/mod.rs b/isideload/src/sideload/mod.rs index 2446950..f8f8722 100644 --- a/isideload/src/sideload/mod.rs +++ b/isideload/src/sideload/mod.rs @@ -1,4 +1,4 @@ pub mod builder; -pub mod certificate; +pub mod cert_identity; pub mod sideloader; pub use builder::{SideloaderBuilder, TeamSelection}; diff --git a/isideload/src/sideload/sideloader.rs b/isideload/src/sideload/sideloader.rs index 39bbaed..a164e6e 100644 --- a/isideload/src/sideload/sideloader.rs +++ b/isideload/src/sideload/sideloader.rs @@ -4,7 +4,7 @@ use crate::{ devices::DevicesApi, teams::{DeveloperTeam, TeamsApi}, }, - sideload::{TeamSelection, builder::MaxCertsBehavior, certificate::CertificateIdentity}, + sideload::{TeamSelection, builder::MaxCertsBehavior, cert_identity::CertificateIdentity}, util::{device::IdeviceInfo, storage::SideloadingStorage}, }; @@ -24,6 +24,9 @@ pub struct Sideloader { } impl Sideloader { + /// Construct a new `Sideloader` instance with the provided configuration + /// + /// See [`crate::sideload::SideloaderBuilder`] for more details and a more convenient way to construct a `Sideloader`. pub fn new( dev_session: DeveloperSession, apple_email: String, @@ -42,6 +45,7 @@ impl Sideloader { } } + /// Sign and install an app pub async fn install_app( &mut self, device_provider: &impl IdeviceProvider, @@ -73,6 +77,7 @@ impl Sideloader { Ok(()) } + /// Get the developer team according to the configured team selection behavior pub async fn get_team(&mut self) -> Result { let teams = self.dev_session.list_teams().await?; Ok(match teams.len() { From 5f7dc3744bf1d6236bb2f2db8f28ca78ab22a31e Mon Sep 17 00:00:00 2001 From: nab138 Date: Mon, 9 Feb 2026 17:53:17 -0500 Subject: [PATCH 51/71] Add p12 export option --- Cargo.lock | 282 ++++++++++++++++++++++-- Cargo.toml | 3 +- isideload/Cargo.toml | 10 +- isideload/src/sideload/cert_identity.rs | 33 +++ 4 files changed, 304 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 689420d..18255bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,7 +15,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b657e772794c6b04730ea897b66a058ccd866c16d1967da05eeeecec39043fe" dependencies = [ "crypto-common 0.2.0", - "inout", + "inout 0.2.2", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher 0.4.4", + "cpufeatures 0.2.17", ] [[package]] @@ -24,7 +35,7 @@ version = "0.9.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04097e08a47d9ad181c2e1f4a5fabc9ae06ce8839a333ba9a949bcb0d31fd2a3" dependencies = [ - "cipher", + "cipher 0.5.0", "cpubits", "cpufeatures 0.2.17", ] @@ -36,8 +47,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e22c0c90bbe8d4f77c3ca9ddabe41a1f8382d6fc1f7cea89459d0f320371f972" dependencies = [ "aead", - "aes", - "cipher", + "aes 0.9.0-rc.4", + "cipher 0.5.0", "ctr", "ghash", "subtle", @@ -207,6 +218,15 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + [[package]] name = "block-padding" version = "0.4.2" @@ -234,13 +254,22 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher 0.4.4", +] + [[package]] name = "cbc" version = "0.2.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85742c5f1d0dda799d2e582c76b82b817d3e4d6434dd285e48e90ed0c963b667" dependencies = [ - "cipher", + "cipher 0.5.0", ] [[package]] @@ -295,6 +324,16 @@ dependencies = [ "windows-link", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common 0.1.7", + "inout 0.1.4", +] + [[package]] name = "cipher" version = "0.5.0" @@ -303,7 +342,7 @@ checksum = "64727038c8c5e2bb503a15b9f5b9df50a1da9a33e83e1f93067d914f2c6604a5" dependencies = [ "block-buffer 0.11.0", "crypto-common 0.2.0", - "inout", + "inout 0.2.2", ] [[package]] @@ -321,6 +360,18 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de0758edba32d61d1fd9f4d69491b47604b91ee2f7e6b33de7e54ca4ebe55dc3" +[[package]] +name = "cms" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b77c319abfd5219629c45c34c89ba945ed3c5e49fcde9d16b6c3885f118a730" +dependencies = [ + "const-oid 0.9.6", + "der 0.7.10", + "spki 0.7.3", + "x509-cert", +] + [[package]] name = "combine" version = "4.6.7" @@ -472,7 +523,7 @@ version = "0.10.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65ea71550d18331d179854662ab330bb54306b9b56020d0466aae2a58f4e17c1" dependencies = [ - "cipher", + "cipher 0.5.0", ] [[package]] @@ -518,6 +569,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid 0.9.6", + "der_derive", + "flagset", + "pem-rfc7468 0.7.0", "zeroize", ] @@ -528,7 +582,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02c1d73e9668ea6b6a28172aa55f3ebec38507131ce179051c8033b5c6037653" dependencies = [ "const-oid 0.10.2", - "pem-rfc7468", + "pem-rfc7468 1.0.0", "zeroize", ] @@ -546,6 +600,17 @@ dependencies = [ "rusticata-macros", ] +[[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", +] + [[package]] name = "deranged" version = "0.5.5" @@ -555,6 +620,15 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "des" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" +dependencies = [ + "cipher 0.4.4", +] + [[package]] name = "digest" version = "0.10.7" @@ -563,6 +637,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", "crypto-common 0.1.7", + "subtle", ] [[package]] @@ -615,6 +690,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[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.9" @@ -847,6 +928,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "hmac" version = "0.13.0-rc.5" @@ -1129,13 +1219,23 @@ dependencies = [ "serde_core", ] +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "block-padding 0.3.3", + "generic-array", +] + [[package]] name = "inout" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" dependencies = [ - "block-padding", + "block-padding 0.4.2", "hybrid-array", ] @@ -1159,17 +1259,18 @@ dependencies = [ name = "isideload" version = "0.2.0" dependencies = [ - "aes", + "aes 0.9.0-rc.4", "aes-gcm", "async-trait", "base64", - "cbc", + "cbc 0.2.0-rc.3", "futures-util", "hex", - "hmac", + "hmac 0.13.0-rc.5", "idevice", "keyring", - "pbkdf2", + "p12-keystore", + "pbkdf2 0.13.0-rc.9", "plist", "plist-macro", "rand 0.10.0", @@ -1179,7 +1280,7 @@ dependencies = [ "rsa", "serde", "serde_json", - "sha2", + "sha2 0.11.0-rc.5", "srp", "thiserror 2.0.18", "tokio", @@ -1440,6 +1541,39 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" +[[package]] +name = "p12-keystore" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d55319bae67f92141ce4da80c5392acd3d1323bd8312c1ffdfb018927d07d7" +dependencies = [ + "base64", + "cbc 0.1.2", + "cms", + "der 0.7.10", + "des", + "hex", + "hmac 0.12.1", + "pkcs12", + "pkcs5", + "rand 0.9.2", + "rc2", + "sha1", + "sha2 0.10.9", + "thiserror 2.0.18", + "x509-parser 0.17.0", +] + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest 0.10.7", + "hmac 0.12.1", +] + [[package]] name = "pbkdf2" version = "0.13.0-rc.9" @@ -1447,7 +1581,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8dfa4e14084d963d35bfb4cdb38712cde78dcf83054c0e8b9b8e899150f374e" dependencies = [ "digest 0.11.0-rc.11", - "hmac", + "hmac 0.13.0-rc.5", ] [[package]] @@ -1460,6 +1594,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "pem-rfc7468" version = "1.0.0" @@ -1497,6 +1640,36 @@ dependencies = [ "spki 0.8.0-rc.4", ] +[[package]] +name = "pkcs12" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "695b3df3d3cc1015f12d70235e35b6b79befc5fa7a9b95b951eab1dd07c9efc2" +dependencies = [ + "cms", + "const-oid 0.9.6", + "der 0.7.10", + "digest 0.10.7", + "spki 0.7.3", + "x509-cert", + "zeroize", +] + +[[package]] +name = "pkcs5" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" +dependencies = [ + "aes 0.8.4", + "cbc 0.1.2", + "der 0.7.10", + "pbkdf2 0.12.2", + "scrypt", + "sha2 0.10.9", + "spki 0.7.3", +] + [[package]] name = "pkcs8" version = "0.11.0-rc.10" @@ -1528,8 +1701,9 @@ dependencies = [ [[package]] name = "plist-macro" -version = "0.1.3" -source = "git+https://github.com/nab138/plist_macro?branch=master#b664a2bc4724d8c5e27a888747400849215c23f0" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ef85ea642f18d31023a2b2c4a3fd823a985b0ab344d1a8fb70d5e122c28544" dependencies = [ "plist", ] @@ -1723,6 +1897,15 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" +[[package]] +name = "rc2" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62c64daa8e9438b84aaae55010a93f396f8e60e3911590fcba770d04643fc1dd" +dependencies = [ + "cipher 0.4.4", +] + [[package]] name = "rcgen" version = "0.14.7" @@ -1733,15 +1916,15 @@ dependencies = [ "pem", "rustls-pki-types", "time", - "x509-parser", + "x509-parser 0.18.1", "yasna", ] [[package]] name = "reqwest" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e9018c9d814e5f30cc16a0f03271aeab3571e609612d9fe78c1aa8d11c2f62" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" dependencies = [ "base64", "bytes", @@ -1925,6 +2108,15 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher 0.4.4", +] + [[package]] name = "same-file" version = "1.0.6" @@ -1943,6 +2135,17 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "pbkdf2 0.12.2", + "salsa20", + "sha2 0.10.9", +] + [[package]] name = "security-framework" version = "2.11.1" @@ -2049,6 +2252,17 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + [[package]] name = "sha2" version = "0.11.0-rc.5" @@ -3200,6 +3414,17 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +[[package]] +name = "x509-cert" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" +dependencies = [ + "const-oid 0.9.6", + "der 0.7.10", + "spki 0.7.3", +] + [[package]] name = "x509-certificate" version = "0.25.0" @@ -3219,6 +3444,23 @@ dependencies = [ "zeroize", ] +[[package]] +name = "x509-parser" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror 2.0.18", + "time", +] + [[package]] name = "x509-parser" version = "0.18.1" diff --git a/Cargo.toml b/Cargo.toml index 5fb16d0..9dc06e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,4 @@ members = ["examples/minimal","isideload"] default-members = ["isideload"] [patch.crates-io] -rustls-platform-verifier = { git = "https://github.com/cstkingkey/rustls-platform-verifier", branch = "extra" } -plist-macro = { git = "https://github.com/nab138/plist_macro", branch = "master" } \ No newline at end of file +rustls-platform-verifier = { git = "https://github.com/cstkingkey/rustls-platform-verifier", branch = "extra" } \ No newline at end of file diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index d36264c..601ac6a 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -14,12 +14,16 @@ readme = "../README.md" default = ["install", "keyring-storage"] install = ["dep:idevice"] keyring-storage = ["keyring"] +p12 = ["dep:p12-keystore"] +# Unfortunately, dependencies are kinda a mess rn, since this requires a beta version of the srp crate. +# Once that becomes stable, hopefuly duplicate dependencies should clean up.\ +# Until then, I will wince in pain every time I see how long the output of cargo tree -d is. [dependencies] idevice = { version = "0.1.52", optional = true } plist = "1.8" -plist-macro = "0.1.3" -reqwest = { version = "0.13.1", features = ["json", "gzip"] } +plist-macro = "0.1.4" +reqwest = { version = "0.13.2", features = ["json", "gzip"] } thiserror = "2.0.17" async-trait = "0.1.89" serde = "1.0.228" @@ -42,5 +46,7 @@ aes-gcm = "0.11.0-rc.3" rsa = { version = "0.10.0-rc.15" } tokio = "1.49.0" keyring = { version = "3.6.3", features = ["apple-native", "linux-native-sync-persistent", "windows-native"], optional = true } +# TODO: Fork to update dependencies (doubt it will ever be updated) x509-certificate = "0.25" rcgen = { version = "0.14.7", default-features = false, features = ["aws_lc_rs", "pem"] } +p12-keystore = { optional = true, version = "0.2.0" } \ No newline at end of file diff --git a/isideload/src/sideload/cert_identity.rs b/isideload/src/sideload/cert_identity.rs index b8b511c..29873ac 100644 --- a/isideload/src/sideload/cert_identity.rs +++ b/isideload/src/sideload/cert_identity.rs @@ -29,6 +29,38 @@ pub struct CertificateIdentity { } impl CertificateIdentity { + // This implementation was "heavily inspired" by Impactor (https://github.com/khcrysalis/Impactor/blob/main/crates/plume_core/src/utils/certificate.rs) + // It's a little messy and I will clean it up when the rust crypto ecosystem gets through it's next release cycle and I can reduce duplicate dependencies + #[cfg(feature = "p12")] + /// Exports the certificate and private key as a PKCS#12 archive + /// If you plan to import into SideStore/AltStore, use the machine id as the password + pub async fn as_p12(&self, password: &str) -> Result, Report> { + let cert_der = self.certificate.encode_der()?; + let key_der = self.private_key.to_pkcs8_der()?.as_bytes().to_vec(); + + let cert = p12_keystore::Certificate::from_der(&cert_der) + .map_err(|e| report!("Failed to parse certificate: {:?}", e))?; + + let local_key_id = { + let mut hasher = Sha256::new(); + hasher.update(&key_der); + let hash = hasher.finalize(); + hash[..8].to_vec() + }; + + let key_chain = p12_keystore::PrivateKeyChain::new(key_der, local_key_id, vec![cert]); + + let mut keystore = p12_keystore::KeyStore::new(); + keystore.add_entry( + "isideload", + p12_keystore::KeyStoreEntry::PrivateKeyChain(key_chain), + ); + + let writer = keystore.writer(&password); + let p12 = writer.write().context("Failed to write PKCS#12 archive")?; + Ok(p12) + } + pub async fn retrieve( machine_name: &str, apple_email: &str, @@ -83,6 +115,7 @@ impl CertificateIdentity { let private_key = storage.retrieve_data(&format!("{}/key", email_hash))?; if private_key.is_some() { + info!("Using existing private key from storage"); return Ok(RsaPrivateKey::from_pkcs8_der(&private_key.unwrap())?); } From 0cee867ca21497f905f0b5a47498bfcbf75bbe05 Mon Sep 17 00:00:00 2001 From: nab138 Date: Tue, 10 Feb 2026 08:28:36 -0500 Subject: [PATCH 52/71] add bundle and application structs --- Cargo.lock | 40 ++++++ isideload/Cargo.toml | 3 +- isideload/src/lib.rs | 3 + isideload/src/sideload/application.rs | 86 ++++++++++++ isideload/src/sideload/bundle.rs | 190 ++++++++++++++++++++++++++ isideload/src/sideload/mod.rs | 2 + isideload/src/sideload/sideloader.rs | 13 +- 7 files changed, 331 insertions(+), 6 deletions(-) create mode 100644 isideload/src/sideload/application.rs create mode 100644 isideload/src/sideload/bundle.rs diff --git a/Cargo.lock b/Cargo.lock index 18255bb..bba36e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -704,6 +704,7 @@ checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", + "zlib-rs", ] [[package]] @@ -1288,6 +1289,7 @@ dependencies = [ "tracing", "uuid", "x509-certificate", + "zip", ] [[package]] @@ -2740,6 +2742,12 @@ dependencies = [ "utf-8", ] +[[package]] +name = "typed-path" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3015e6ce46d5ad8751e4a772543a30c7511468070e98e64e20165f8f81155b64" + [[package]] name = "typenum" version = "1.19.0" @@ -3605,8 +3613,40 @@ dependencies = [ "syn", ] +[[package]] +name = "zip" +version = "7.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc12baa6db2b15a140161ce53d72209dacea594230798c24774139b54ecaa980" +dependencies = [ + "crc32fast", + "flate2", + "indexmap", + "memchr", + "typed-path", + "zopfli", +] + +[[package]] +name = "zlib-rs" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7948af682ccbc3342b6e9420e8c51c1fe5d7bf7756002b4a3c6cabfe96a7e3c" + [[package]] name = "zmij" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445" + +[[package]] +name = "zopfli" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 601ac6a..a03ecf4 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -49,4 +49,5 @@ keyring = { version = "3.6.3", features = ["apple-native", "linux-native-sync-pe # TODO: Fork to update dependencies (doubt it will ever be updated) x509-certificate = "0.25" rcgen = { version = "0.14.7", default-features = false, features = ["aws_lc_rs", "pem"] } -p12-keystore = { optional = true, version = "0.2.0" } \ No newline at end of file +p12-keystore = { optional = true, version = "0.2.0" } +zip = { version = "7.4", default-features = false, features = ["deflate"] } \ No newline at end of file diff --git a/isideload/src/lib.rs b/isideload/src/lib.rs index 0c73bc3..13d7121 100644 --- a/isideload/src/lib.rs +++ b/isideload/src/lib.rs @@ -22,6 +22,9 @@ pub enum SideloadError { #[error("Developer error {0}: {1}")] DeveloperError(i64, String), + + #[error("Invalid bundle: {0}")] + InvalidBundle(String), } // The default reqwest error formatter sucks and provides no info diff --git a/isideload/src/sideload/application.rs b/isideload/src/sideload/application.rs new file mode 100644 index 0000000..91ba3fd --- /dev/null +++ b/isideload/src/sideload/application.rs @@ -0,0 +1,86 @@ +// This file was made using https://github.com/Dadoum/Sideloader as a reference. +// I'm planning on redoing this later to better handle entitlements, extensions, etc, but it will do for now + +use crate::SideloadError; +use crate::sideload::bundle::Bundle; +use rootcause::prelude::*; +use std::fs::File; +use std::path::PathBuf; +use zip::ZipArchive; + +pub struct Application { + pub bundle: Bundle, + //pub temp_path: PathBuf, +} + +impl Application { + pub fn new(path: PathBuf) -> Result { + if !path.exists() { + bail!(SideloadError::InvalidBundle( + "Application path does not exist".to_string(), + )); + } + + let mut bundle_path = path.clone(); + //let mut temp_path = PathBuf::new(); + + if path.is_file() { + let temp_dir = std::env::temp_dir(); + let temp_path = temp_dir + .join(path.file_name().unwrap().to_string_lossy().to_string() + "_extracted"); + if temp_path.exists() { + std::fs::remove_dir_all(&temp_path) + .context("Failed to remove existing temporary directory")?; + } + std::fs::create_dir_all(&temp_path).context("Failed to create temporary directory")?; + + let file = File::open(&path).context("Failed to open application archive")?; + let mut archive = + ZipArchive::new(file).context("Failed to open application archive")?; + archive + .extract(&temp_path) + .context("Failed to extract application archive")?; + + let payload_folder = temp_path.join("Payload"); + if payload_folder.exists() && payload_folder.is_dir() { + let app_dirs: Vec<_> = std::fs::read_dir(&payload_folder) + .context("Failed to read Payload directory")? + .filter_map(Result::ok) + .filter(|entry| entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false)) + .filter(|entry| entry.path().extension().is_some_and(|ext| ext == "app")) + .collect(); + if app_dirs.len() == 1 { + bundle_path = app_dirs[0].path(); + } else if app_dirs.is_empty() { + bail!(SideloadError::InvalidBundle( + "No .app directory found in Payload".to_string(), + )); + } else { + bail!(SideloadError::InvalidBundle( + "Multiple .app directories found in Payload".to_string(), + )); + } + } else { + bail!(SideloadError::InvalidBundle( + "No Payload directory found in the application archive".to_string(), + )); + } + } + let bundle = Bundle::new(bundle_path)?; + + Ok(Application { + bundle, /*temp_path*/ + }) + } + + pub fn is_sidestore(&self) -> bool { + self.bundle.bundle_identifier().unwrap_or("") == "com.SideStore.SideStore" + } + + pub fn is_lc_and_sidestore(&self) -> bool { + self.bundle + .frameworks() + .iter() + .any(|f| f.bundle_identifier().unwrap_or("") == "com.SideStore.SideStore") + } +} diff --git a/isideload/src/sideload/bundle.rs b/isideload/src/sideload/bundle.rs new file mode 100644 index 0000000..62f9732 --- /dev/null +++ b/isideload/src/sideload/bundle.rs @@ -0,0 +1,190 @@ +// This file was made using https://github.com/Dadoum/Sideloader as a reference. +// I'm planning on redoing this later to better handle entitlements, extensions, etc, but it will do for now + +use plist::{Dictionary, Value}; +use rootcause::prelude::*; +use std::{ + fs, + path::{Path, PathBuf}, +}; + +use crate::SideloadError; + +#[derive(Debug)] +pub struct Bundle { + pub app_info: Dictionary, + pub bundle_dir: PathBuf, + + app_extensions: Vec, + frameworks: Vec, + _libraries: Vec, +} + +impl Bundle { + pub fn new(bundle_dir: PathBuf) -> Result { + let mut bundle_path = bundle_dir; + // Remove trailing slash/backslash + if let Some(path_str) = bundle_path.to_str() + && (path_str.ends_with('/') || path_str.ends_with('\\')) + { + bundle_path = PathBuf::from(&path_str[..path_str.len() - 1]); + } + + let info_plist_path = bundle_path.join("Info.plist"); + assert_bundle( + info_plist_path.exists(), + &format!("No Info.plist here: {}", info_plist_path.display()), + )?; + + let plist_data = fs::read(&info_plist_path).context(SideloadError::InvalidBundle( + "Failed to read Info.plist".to_string(), + ))?; + + let app_info = plist::from_bytes(&plist_data).context(SideloadError::InvalidBundle( + "Failed to parse Info.plist".to_string(), + ))?; + + // Load app extensions from PlugIns directory + let plug_ins_dir = bundle_path.join("PlugIns"); + let app_extensions = if plug_ins_dir.exists() { + fs::read_dir(&plug_ins_dir) + .context(SideloadError::InvalidBundle( + "Failed to read PlugIns directory".to_string(), + ))? + .filter_map(|entry| entry.ok()) + .filter(|entry| { + entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false) + && entry.path().join("Info.plist").exists() + }) + .filter_map(|entry| Bundle::new(entry.path()).ok()) + .collect() + } else { + Vec::new() + }; + + // Load frameworks from Frameworks directory + let frameworks_dir = bundle_path.join("Frameworks"); + let frameworks = if frameworks_dir.exists() { + fs::read_dir(&frameworks_dir) + .context(SideloadError::InvalidBundle( + "Failed to read Frameworks directory".to_string(), + ))? + .filter_map(|entry| entry.ok()) + .filter(|entry| { + entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false) + && entry.path().join("Info.plist").exists() + }) + .filter_map(|entry| Bundle::new(entry.path()).ok()) + .collect() + } else { + Vec::new() + }; + + // Find all .dylib files in the bundle directory (recursive) + let libraries = find_dylibs(&bundle_path, &bundle_path)?; + + Ok(Bundle { + app_info, + bundle_dir: bundle_path, + app_extensions, + frameworks, + _libraries: libraries, + }) + } + + pub fn set_bundle_identifier(&mut self, id: &str) { + self.app_info.insert( + "CFBundleIdentifier".to_string(), + Value::String(id.to_string()), + ); + } + + pub fn bundle_identifier(&self) -> Option<&str> { + self.app_info + .get("CFBundleIdentifier") + .and_then(|v| v.as_string()) + } + + pub fn bundle_name(&self) -> Option<&str> { + self.app_info + .get("CFBundleName") + .and_then(|v| v.as_string()) + } + + pub fn app_extensions(&self) -> &[Bundle] { + &self.app_extensions + } + + pub fn app_extensions_mut(&mut self) -> &mut [Bundle] { + &mut self.app_extensions + } + + pub fn frameworks(&self) -> &[Bundle] { + &self.frameworks + } + + pub fn frameworks_mut(&mut self) -> &mut [Bundle] { + &mut self.frameworks + } + + pub fn write_info(&self) -> Result<(), Report> { + let info_plist_path = self.bundle_dir.join("Info.plist"); + plist::to_file_binary(&info_plist_path, &self.app_info).context( + SideloadError::InvalidBundle("Failed to write Info.plist".to_string()), + )?; + Ok(()) + } +} + +fn assert_bundle(condition: bool, msg: &str) -> Result<(), Report> { + if !condition { + bail!(SideloadError::InvalidBundle(msg.to_string())) + } else { + Ok(()) + } +} + +fn find_dylibs(dir: &Path, bundle_root: &Path) -> Result, Report> { + let mut libraries = Vec::new(); + + fn collect_dylibs( + dir: &Path, + bundle_root: &Path, + libraries: &mut Vec, + ) -> Result<(), Report> { + let entries = fs::read_dir(dir).context(SideloadError::InvalidBundle(format!( + "Failed to read directory {}", + dir.display() + )))?; + + for entry in entries { + let entry = entry.context(SideloadError::InvalidBundle( + "Failed to read directory entry".to_string(), + ))?; + + let path = entry.path(); + let file_type = entry.file_type().context(SideloadError::InvalidBundle( + "Failed to get file type".to_string(), + ))?; + + if file_type.is_file() { + if let Some(name) = path.file_name().and_then(|n| n.to_str()) + && name.ends_with(".dylib") + { + // Get relative path from bundle root + if let Ok(relative_path) = path.strip_prefix(bundle_root) + && let Some(relative_str) = relative_path.to_str() + { + libraries.push(relative_str.to_string()); + } + } + } else if file_type.is_dir() { + collect_dylibs(&path, bundle_root, libraries)?; + } + } + Ok(()) + } + + collect_dylibs(dir, bundle_root, &mut libraries)?; + Ok(libraries) +} diff --git a/isideload/src/sideload/mod.rs b/isideload/src/sideload/mod.rs index f8f8722..c194dc1 100644 --- a/isideload/src/sideload/mod.rs +++ b/isideload/src/sideload/mod.rs @@ -1,4 +1,6 @@ +pub mod application; pub mod builder; +pub mod bundle; pub mod cert_identity; pub mod sideloader; pub use builder::{SideloaderBuilder, TeamSelection}; diff --git a/isideload/src/sideload/sideloader.rs b/isideload/src/sideload/sideloader.rs index a164e6e..9b3d14e 100644 --- a/isideload/src/sideload/sideloader.rs +++ b/isideload/src/sideload/sideloader.rs @@ -4,7 +4,10 @@ use crate::{ devices::DevicesApi, teams::{DeveloperTeam, TeamsApi}, }, - sideload::{TeamSelection, builder::MaxCertsBehavior, cert_identity::CertificateIdentity}, + sideload::{ + TeamSelection, application::Application, builder::MaxCertsBehavior, + cert_identity::CertificateIdentity, + }, util::{device::IdeviceInfo, storage::SideloadingStorage}, }; @@ -69,10 +72,10 @@ impl Sideloader { ) .await?; - // info!( - // "Using certificate for machine {} with ID {}", - // cert_identity.machine_name, cert_identity.machine_id - // ); + let mut app = Application::new(app_path)?; + + let is_sidestore = app.is_sidestore(); + let is_lc_and_sidestore = app.is_lc_and_sidestore(); Ok(()) } From 728c61e4eee7192efdc06110ed45ffc243b56f80 Mon Sep 17 00:00:00 2001 From: nab138 Date: Tue, 10 Feb 2026 11:11:01 -0500 Subject: [PATCH 53/71] register app ids --- README.md | 11 +- isideload/src/dev/app_ids.rs | 27 ++++- isideload/src/sideload/application.rs | 141 +++++++++++++++++++++++++- isideload/src/sideload/builder.rs | 41 ++++++++ isideload/src/sideload/sideloader.rs | 46 ++++++++- isideload/src/util/plist.rs | 8 ++ 6 files changed, 261 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 8cf3427..77069b0 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,14 @@ This branch is home to isideload-next, the next major version of isideload. It f ## TODO -- [ ] Signing apps -- [ ] Installing apps -- [ ] Remove dependency on ring +Things left todo before the rewrite is considered finished + +- Download provisioning profiles +- Signing apps +- Installing apps +(will superceed the original isideload at this point) +- Remove dependency on ring +- More parallelism/cachng for better performance ## Licensing diff --git a/isideload/src/dev/app_ids.rs b/isideload/src/dev/app_ids.rs index c546438..58e6454 100644 --- a/isideload/src/dev/app_ids.rs +++ b/isideload/src/dev/app_ids.rs @@ -4,7 +4,7 @@ use crate::{ device_type::{DeveloperDeviceType, dev_url}, teams::DeveloperTeam, }, - util::plist::SensitivePlistAttachment, + util::plist::{PlistDataExtract, SensitivePlistAttachment}, }; use plist::{Data, Date, Dictionary, Value}; use plist_macro::plist; @@ -17,7 +17,7 @@ pub struct AppId { pub app_id_id: String, pub identifier: String, pub name: String, - pub features: Option, + pub features: Dictionary, pub expiration_date: Option, } @@ -178,3 +178,26 @@ impl AppIdsApi for DeveloperSession { self } } + +impl AppId { + pub async fn ensure_group_feature( + &mut self, + dev_session: &mut DeveloperSession, + team: &DeveloperTeam, + ) -> Result<(), Report> { + let app_group_feature_enabled = self.features.get_bool("APG3427HIY")?; + + if !app_group_feature_enabled { + let body = plist!(dict { + "APG3427HIY": true, + }); + let new_features = dev_session + .update_app_id(team, self, body, None) + .await? + .features; + self.features = new_features; + } + + Ok(()) + } +} diff --git a/isideload/src/sideload/application.rs b/isideload/src/sideload/application.rs index 91ba3fd..129dd08 100644 --- a/isideload/src/sideload/application.rs +++ b/isideload/src/sideload/application.rs @@ -2,7 +2,12 @@ // I'm planning on redoing this later to better handle entitlements, extensions, etc, but it will do for now use crate::SideloadError; +use crate::dev::app_ids::{AppId, AppIdsApi}; +use crate::dev::developer_session::DeveloperSession; +use crate::dev::teams::DeveloperTeam; +use crate::sideload::builder::ExtensionsBehavior; use crate::sideload::bundle::Bundle; +use rootcause::option_ext::OptionExt; use rootcause::prelude::*; use std::fs::File; use std::path::PathBuf; @@ -73,14 +78,140 @@ impl Application { }) } - pub fn is_sidestore(&self) -> bool { - self.bundle.bundle_identifier().unwrap_or("") == "com.SideStore.SideStore" - } + pub fn get_special_app(&self) -> Option { + let special_app = match self.bundle.bundle_identifier().unwrap_or("") { + "com.rileytestut.AltStore" => Some(SpecialApp::AltStore), + "com.SideStore.SideStore" => Some(SpecialApp::SideStore), + _ => None, + }; + if special_app.is_some() { + return special_app; + } - pub fn is_lc_and_sidestore(&self) -> bool { - self.bundle + if self + .bundle .frameworks() .iter() .any(|f| f.bundle_identifier().unwrap_or("") == "com.SideStore.SideStore") + { + return Some(SpecialApp::SideStoreLc); + } + + None + } + + pub fn main_bundle_id(&self) -> Result { + let str = self + .bundle + .bundle_identifier() + .ok_or_report() + .context("Failed to get main bundle identifier")? + .to_string(); + + Ok(str) + } + + pub fn main_app_name(&self) -> Result { + let str = self + .bundle + .bundle_name() + .ok_or_report() + .context("Failed to get main app name")? + .to_string(); + + Ok(str) + } + + pub fn update_bundle_id( + &mut self, + main_app_bundle_id: &str, + main_app_id_str: &str, + ) -> Result<(), Report> { + let extensions = self.bundle.app_extensions_mut(); + 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()) { + bail!(SideloadError::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!( + "{}{}", + main_app_id_str, + &id[main_app_bundle_id.len()..] + )); + } + } + } + self.bundle.set_bundle_identifier(&main_app_id_str); + + Ok(()) + } + + pub async fn register_app_ids( + &self, + mode: &ExtensionsBehavior, + dev_session: &mut DeveloperSession, + team: &DeveloperTeam, + ) -> Result, Report> { + let extension_refs: Vec<_> = self.bundle.app_extensions().iter().collect(); + let mut bundles_with_app_id = vec![&self.bundle]; + bundles_with_app_id.extend(extension_refs); + + let list_app_ids_response = dev_session + .list_app_ids(&team, None) + .await + .context("Failed to list app IDs for the developer team")?; + let app_ids_to_register = match mode { + ExtensionsBehavior::RegisterAll => bundles_with_app_id + .iter() + .filter(|bundle| { + let bundle_id = bundle.bundle_identifier().unwrap_or(""); + !list_app_ids_response + .app_ids + .iter() + .any(|app_id| app_id.identifier == bundle_id) + }) + .collect::>(), + _ => todo!(), + }; + + if let Some(available) = list_app_ids_response.available_quantity + && app_ids_to_register.len() > available.try_into().unwrap() + { + bail!( + "Not enough available app IDs. {} are required, but only {} are available.", + app_ids_to_register.len(), + available + ); + } + + for bundle in app_ids_to_register { + let id = bundle.bundle_identifier().unwrap_or(""); + let name = bundle.bundle_name().unwrap_or(""); + dev_session.add_app_id(&team, name, id, None).await?; + } + let list_app_id_response = dev_session.list_app_ids(&team, None).await?; + let app_ids: Vec<_> = list_app_id_response + .app_ids + .into_iter() + .filter(|app_id| { + bundles_with_app_id + .iter() + .any(|bundle| app_id.identifier == bundle.bundle_identifier().unwrap_or("")) + }) + .collect(); + + Ok(app_ids) } } + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SpecialApp { + SideStore, + SideStoreLc, + AltStore, + StikStore, +} diff --git a/isideload/src/sideload/builder.rs b/isideload/src/sideload/builder.rs index 776e218..fa70169 100644 --- a/isideload/src/sideload/builder.rs +++ b/isideload/src/sideload/builder.rs @@ -39,11 +39,44 @@ pub enum MaxCertsBehavior { Prompt(fn(&Vec) -> Option>), } +/// The actual behavior choices for extensions (non-prompt variants) +pub enum ExtensionsBehaviorChoice { + /// Use the main app id/profile for all sub-bundles + ReuseMain, + /// Create separate app ids/profiles for each sub-bundle + RegisterAll, + /// Remove all sub-bundles + RemoveExtensions, +} + +/// Behavior used when an app contains sub bundles +pub enum ExtensionsBehavior { + /// Use the main app id/profile for all sub-bundles + ReuseMain, + /// Create separate app ids/profiles for each sub-bundle + RegisterAll, + /// Remove all sub-bundles + RemoveExtensions, + /// Prompt the user to choose one of the above behaviors + Prompt(fn(&Vec) -> ExtensionsBehaviorChoice), +} + +impl From for ExtensionsBehavior { + fn from(choice: ExtensionsBehaviorChoice) -> Self { + match choice { + ExtensionsBehaviorChoice::ReuseMain => ExtensionsBehavior::ReuseMain, + ExtensionsBehaviorChoice::RegisterAll => ExtensionsBehavior::RegisterAll, + ExtensionsBehaviorChoice::RemoveExtensions => ExtensionsBehavior::RemoveExtensions, + } + } +} + pub struct SideloaderBuilder { developer_session: DeveloperSession, apple_email: String, team_selection: Option, max_certs_behavior: Option, + extensions_behavior: Option, storage: Option>, machine_name: Option, } @@ -57,6 +90,7 @@ impl SideloaderBuilder { machine_name: None, apple_email, max_certs_behavior: None, + extensions_behavior: None, } } @@ -94,6 +128,11 @@ impl SideloaderBuilder { self } + pub fn extensions_behavior(mut self, behavior: ExtensionsBehavior) -> Self { + self.extensions_behavior = Some(behavior); + self + } + /// Build the `Sideloader` instance with the provided configuration pub fn build(self) -> Sideloader { Sideloader::new( @@ -104,6 +143,8 @@ impl SideloaderBuilder { self.machine_name.unwrap_or_else(|| "isideload".to_string()), self.storage .unwrap_or_else(|| Box::new(crate::util::storage::new_storage())), + self.extensions_behavior + .unwrap_or(ExtensionsBehavior::RegisterAll), ) } } diff --git a/isideload/src/sideload/sideloader.rs b/isideload/src/sideload/sideloader.rs index 9b3d14e..a56ba39 100644 --- a/isideload/src/sideload/sideloader.rs +++ b/isideload/src/sideload/sideloader.rs @@ -5,7 +5,9 @@ use crate::{ teams::{DeveloperTeam, TeamsApi}, }, sideload::{ - TeamSelection, application::Application, builder::MaxCertsBehavior, + TeamSelection, + application::{Application, SpecialApp}, + builder::{ExtensionsBehavior, MaxCertsBehavior}, cert_identity::CertificateIdentity, }, util::{device::IdeviceInfo, storage::SideloadingStorage}, @@ -24,6 +26,7 @@ pub struct Sideloader { machine_name: String, apple_email: String, max_certs_behavior: MaxCertsBehavior, + extensions_behavior: ExtensionsBehavior, } impl Sideloader { @@ -37,6 +40,7 @@ impl Sideloader { max_certs_behavior: MaxCertsBehavior, machine_name: String, storage: Box, + extensions_behavior: ExtensionsBehavior, ) -> Self { Sideloader { team_selection, @@ -45,6 +49,7 @@ impl Sideloader { machine_name, apple_email, max_certs_behavior, + extensions_behavior, } } @@ -73,9 +78,44 @@ impl Sideloader { .await?; let mut app = Application::new(app_path)?; + let special = app.get_special_app(); - let is_sidestore = app.is_sidestore(); - let is_lc_and_sidestore = app.is_lc_and_sidestore(); + let main_bundle_id = app.main_bundle_id()?; + let main_app_name = app.main_app_name()?; + let main_app_id_str = format!("{}.{}", main_bundle_id, team.team_id); + app.update_bundle_id(&main_bundle_id, &main_app_id_str)?; + let mut app_ids = app + .register_app_ids(&self.extensions_behavior, &mut self.dev_session, &team) + .await?; + let main_app_id = match app_ids + .iter() + .find(|app_id| app_id.identifier == main_app_id_str) + { + Some(id) => id, + None => { + bail!( + "Main app ID {} not found in registered app IDs", + main_app_id_str + ); + } + }; + + for app_id in app_ids.iter_mut() { + app_id + .ensure_group_feature(&mut self.dev_session, &team) + .await?; + + // TODO: Increased memory entitlement + } + + let group_identifier = format!( + "group.{}", + if Some(SpecialApp::SideStoreLc) == special { + format!("com.SideStore.SideStore.{}", team.team_id) + } else { + main_app_id_str.clone() + } + ); Ok(()) } diff --git a/isideload/src/util/plist.rs b/isideload/src/util/plist.rs index be54ecf..6f7a8e0 100644 --- a/isideload/src/util/plist.rs +++ b/isideload/src/util/plist.rs @@ -55,6 +55,7 @@ pub trait PlistDataExtract { fn get_string(&self, key: &str) -> Result; fn get_signed_integer(&self, key: &str) -> Result; fn get_dict(&self, key: &str) -> Result<&Dictionary, Report>; + fn get_bool(&self, key: &str) -> Result; fn get_struct(&self, key: &str) -> Result; } @@ -120,4 +121,11 @@ impl PlistDataExtract for Dictionary { })?; Ok(struct_data) } + + fn get_bool(&self, key: &str) -> Result { + self.get(key).and_then(|v| v.as_boolean()).ok_or_else(|| { + report!("Plist missing boolean for key '{}'", key) + .attach(SensitivePlistAttachment::new(self.clone())) + }) + } } From d007f259c126bf2aa07dc2a1fad1bf679dad942b Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 13 Feb 2026 08:20:42 -0500 Subject: [PATCH 54/71] Assign app groups and download provisioning profile --- isideload/src/dev/app_groups.rs | 23 +++++++++++++++++++ isideload/src/sideload/sideloader.rs | 33 +++++++++++++++++++++------- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/isideload/src/dev/app_groups.rs b/isideload/src/dev/app_groups.rs index 881153b..c17d2a9 100644 --- a/isideload/src/dev/app_groups.rs +++ b/isideload/src/dev/app_groups.rs @@ -7,6 +7,7 @@ use crate::dev::{ use plist_macro::plist; use rootcause::prelude::*; use serde::Deserialize; +use tracing::info; #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] @@ -91,6 +92,28 @@ pub trait AppGroupsApi { Ok(()) } + + async fn ensure_app_group( + &mut self, + team: &DeveloperTeam, + name: &str, + identifier: &str, + device_type: impl Into> + Send, + ) -> Result { + let device_type = device_type.into(); + let groups = self.list_app_groups(team, device_type.clone()).await?; + let matching_group = groups.iter().find(|g| g.identifier == identifier); + + if let Some(group) = matching_group { + Ok(group.clone()) + } else { + info!("Adding application group"); + let group = self + .add_app_group(team, name, identifier, device_type) + .await?; + Ok(group) + } + } } impl AppGroupsApi for DeveloperSession { diff --git a/isideload/src/sideload/sideloader.rs b/isideload/src/sideload/sideloader.rs index a56ba39..f8e808e 100644 --- a/isideload/src/sideload/sideloader.rs +++ b/isideload/src/sideload/sideloader.rs @@ -1,5 +1,7 @@ use crate::{ dev::{ + app_groups::AppGroupsApi, + app_ids::AppIdsApi, developer_session::DeveloperSession, devices::DevicesApi, teams::{DeveloperTeam, TeamsApi}, @@ -98,15 +100,8 @@ impl Sideloader { main_app_id_str ); } - }; - - for app_id in app_ids.iter_mut() { - app_id - .ensure_group_feature(&mut self.dev_session, &team) - .await?; - - // TODO: Increased memory entitlement } + .clone(); let group_identifier = format!( "group.{}", @@ -117,6 +112,28 @@ impl Sideloader { } ); + let app_group = self + .dev_session + .ensure_app_group(&team, &main_app_name, &group_identifier, None) + .await?; + + for app_id in app_ids.iter_mut() { + app_id + .ensure_group_feature(&mut self.dev_session, &team) + .await?; + + self.dev_session + .assign_app_group(&team, &app_group, app_id, None) + .await?; + + // TODO: Increased memory entitlement + } + + let provisioning_profile = self + .dev_session + .download_team_provisioning_profile(&team, &main_app_id, None) + .await?; + Ok(()) } From 59985f9b92af4651446beb716d01d4cfe5f65989 Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 13 Feb 2026 09:01:33 -0500 Subject: [PATCH 55/71] Modify bundle for altstore/sidestore/sidstore+lc --- isideload/Cargo.toml | 5 +-- isideload/src/sideload/application.rs | 53 +++++++++++++++++++++++++ isideload/src/sideload/cert_identity.rs | 6 ++- isideload/src/sideload/sideloader.rs | 11 +++++ 4 files changed, 71 insertions(+), 4 deletions(-) diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index a03ecf4..5cb6fc9 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -14,7 +14,6 @@ readme = "../README.md" default = ["install", "keyring-storage"] install = ["dep:idevice"] keyring-storage = ["keyring"] -p12 = ["dep:p12-keystore"] # Unfortunately, dependencies are kinda a mess rn, since this requires a beta version of the srp crate. # Once that becomes stable, hopefuly duplicate dependencies should clean up.\ @@ -44,10 +43,10 @@ cbc = { version = "0.2.0-rc.3", features = ["alloc"] } aes = "0.9.0-rc.4" aes-gcm = "0.11.0-rc.3" rsa = { version = "0.10.0-rc.15" } -tokio = "1.49.0" +tokio = { version = "1.49.0", features = ["fs"] } keyring = { version = "3.6.3", features = ["apple-native", "linux-native-sync-persistent", "windows-native"], optional = true } # TODO: Fork to update dependencies (doubt it will ever be updated) x509-certificate = "0.25" rcgen = { version = "0.14.7", default-features = false, features = ["aws_lc_rs", "pem"] } -p12-keystore = { optional = true, version = "0.2.0" } +p12-keystore = "0.2.0" zip = { version = "7.4", default-features = false, features = ["deflate"] } \ No newline at end of file diff --git a/isideload/src/sideload/application.rs b/isideload/src/sideload/application.rs index 129dd08..692d37f 100644 --- a/isideload/src/sideload/application.rs +++ b/isideload/src/sideload/application.rs @@ -7,10 +7,12 @@ use crate::dev::developer_session::DeveloperSession; use crate::dev::teams::DeveloperTeam; use crate::sideload::builder::ExtensionsBehavior; use crate::sideload::bundle::Bundle; +use crate::sideload::cert_identity::CertificateIdentity; use rootcause::option_ext::OptionExt; use rootcause::prelude::*; use std::fs::File; use std::path::PathBuf; +use tokio::io::AsyncWriteExt; use zip::ZipArchive; pub struct Application { @@ -206,6 +208,57 @@ impl Application { Ok(app_ids) } + + pub async fn apply_special_app_behavior( + &mut self, + special: &Option, + group_identifier: &str, + cert: &CertificateIdentity, + ) -> Result<(), Report> { + if special.is_none() { + return Ok(()); + } + let special = special.as_ref().unwrap(); + + if special == &SpecialApp::SideStoreLc + || special == &SpecialApp::SideStore + || special == &SpecialApp::AltStore + { + self.bundle.app_info.insert( + "ALTAppGroups".to_string(), + plist::Value::Array(vec![plist::Value::String(group_identifier.to_string())]), + ); + + let target_bundle = + match special { + SpecialApp::SideStoreLc => self.bundle.frameworks_mut().iter_mut().find(|fw| { + fw.bundle_identifier().unwrap_or("") == "com.SideStore.SideStore" + }), + _ => Some(&mut self.bundle), + }; + + if let Some(target_bundle) = target_bundle { + target_bundle.app_info.insert( + "ALTCertificateID".to_string(), + plist::Value::String(cert.get_serial_number()), + ); + + let p12_bytes = cert + .as_p12(&cert.machine_id) + .await + .context("Failed to encode cert as p12")?; + let alt_cert_path = target_bundle.bundle_dir.join("ALTCertificate.p12"); + + let mut file = tokio::fs::File::create(&alt_cert_path) + .await + .context("Failed to create ALTCertificate.p12")?; + file.write_all(&p12_bytes) + .await + .context("Failed to write ALTCertificate.p12")?; + } + } + Ok(()) + } } #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/isideload/src/sideload/cert_identity.rs b/isideload/src/sideload/cert_identity.rs index 29873ac..e8a4f09 100644 --- a/isideload/src/sideload/cert_identity.rs +++ b/isideload/src/sideload/cert_identity.rs @@ -1,3 +1,4 @@ +use hex::ToHex; use rcgen::{CertificateParams, DistinguishedName, DnType, KeyPair, PKCS_RSA_SHA256}; use rootcause::prelude::*; use rsa::{ @@ -31,7 +32,6 @@ pub struct CertificateIdentity { impl CertificateIdentity { // This implementation was "heavily inspired" by Impactor (https://github.com/khcrysalis/Impactor/blob/main/crates/plume_core/src/utils/certificate.rs) // It's a little messy and I will clean it up when the rust crypto ecosystem gets through it's next release cycle and I can reduce duplicate dependencies - #[cfg(feature = "p12")] /// Exports the certificate and private key as a PKCS#12 archive /// If you plan to import into SideStore/AltStore, use the machine id as the password pub async fn as_p12(&self, password: &str) -> Result, Report> { @@ -61,6 +61,10 @@ impl CertificateIdentity { Ok(p12) } + pub fn get_serial_number(&self) -> String { + self.certificate.serial_number_asn1().encode_hex() + } + pub async fn retrieve( machine_name: &str, apple_email: &str, diff --git a/isideload/src/sideload/sideloader.rs b/isideload/src/sideload/sideloader.rs index f8e808e..f8ded2a 100644 --- a/isideload/src/sideload/sideloader.rs +++ b/isideload/src/sideload/sideloader.rs @@ -129,11 +129,22 @@ impl Sideloader { // TODO: Increased memory entitlement } + app.apply_special_app_behavior(&special, &group_identifier, &cert_identity) + .await?; + let provisioning_profile = self .dev_session .download_team_provisioning_profile(&team, &main_app_id, None) .await?; + app.bundle.write_info()?; + for ext in app.bundle.app_extensions_mut() { + ext.write_info()?; + } + for ext in app.bundle.frameworks_mut() { + ext.write_info()?; + } + Ok(()) } From c49e07f2ff346a762cf006e2c70a50c956682827 Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 13 Feb 2026 09:04:28 -0500 Subject: [PATCH 56/71] Trim leading zeros in serial number --- isideload/src/sideload/cert_identity.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/isideload/src/sideload/cert_identity.rs b/isideload/src/sideload/cert_identity.rs index e8a4f09..58b605d 100644 --- a/isideload/src/sideload/cert_identity.rs +++ b/isideload/src/sideload/cert_identity.rs @@ -62,7 +62,8 @@ impl CertificateIdentity { } pub fn get_serial_number(&self) -> String { - self.certificate.serial_number_asn1().encode_hex() + let serial: String = self.certificate.serial_number_asn1().encode_hex(); + serial.trim_start_matches('0').to_string() } pub async fn retrieve( From 1a343f8cba8de8875196911c6c71d844019468eb Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 13 Feb 2026 09:35:54 -0500 Subject: [PATCH 57/71] add certs to gitignore --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7b3aad1..a01f781 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ target -state.plist \ No newline at end of file +state.plist + +*.mobileprovision +*.pem \ No newline at end of file From eefbdcafe7850a997b642e7e030e20c19bf0db70 Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 13 Feb 2026 21:41:03 -0500 Subject: [PATCH 58/71] france, china, and linux fixes --- examples/minimal/src/main.rs | 6 +++++- isideload/src/auth/apple_account.rs | 5 ++++- isideload/src/util/keyring_storage.rs | 14 -------------- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 0f326fa..6d740de 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -9,6 +9,7 @@ use isideload::{ teams::DeveloperTeam, }, sideload::{SideloaderBuilder, TeamSelection, builder::MaxCertsBehavior}, + util::keyring_storage::KeyringStorage, }; use tracing::Level; @@ -125,7 +126,10 @@ async fn main() { let mut sideloader = SideloaderBuilder::new(dev_session, apple_id.to_string()) .team_selection(TeamSelection::Prompt(team_selection_prompt)) .max_certs_behavior(MaxCertsBehavior::Prompt(cert_selection_prompt)) - .machine_name("isideload-demo".to_string()) + .storage(Box::new(KeyringStorage::new( + "isideload-minimal".to_string(), + ))) + .machine_name("isideload-minimal".to_string()) .build(); let result = sideloader.install_app(&provider, app_path).await; diff --git a/isideload/src/auth/apple_account.rs b/isideload/src/auth/apple_account.rs index 13f771b..0adea25 100644 --- a/isideload/src/auth/apple_account.rs +++ b/isideload/src/auth/apple_account.rs @@ -457,9 +457,12 @@ impl AppleAccount { debug!("Sending proof login request"); + let mut close_headers = HeaderMap::new(); + close_headers.insert("Connection", HeaderValue::from_static("close")); + let response2 = self .grandslam_client - .plist_request(&gs_service_url, &req2, None) + .plist_request(&gs_service_url, &req2, Some(close_headers)) .await .context("Failed to send proof login request")? .check_grandslam_error() diff --git a/isideload/src/util/keyring_storage.rs b/isideload/src/util/keyring_storage.rs index 7ff4716..0ebaa34 100644 --- a/isideload/src/util/keyring_storage.rs +++ b/isideload/src/util/keyring_storage.rs @@ -43,18 +43,4 @@ impl SideloadingStorage for KeyringStorage { Err(e) => Err(e.into()), } } - - fn store_data(&self, key: &str, value: &[u8]) -> Result<(), Report> { - Entry::new(&self.service_name, key)?.set_secret(value)?; - Ok(()) - } - - fn retrieve_data(&self, key: &str) -> Result>, Report> { - let entry = Entry::new(&self.service_name, key)?; - match entry.get_secret() { - Ok(secret) => Ok(Some(secret)), - Err(keyring::Error::NoEntry) => Ok(None), - Err(e) => Err(e.into()), - } - } } From 81d79ca11b5f6ea79a8e5bc075f9b034245717ec Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 14 Feb 2026 01:10:01 -0500 Subject: [PATCH 59/71] Sign and install apps --- Cargo.lock | 1717 ++++++++++++++++++++++- README.md | 11 +- examples/minimal/src/main.rs | 2 +- isideload/Cargo.toml | 5 +- isideload/src/lib.rs | 4 + isideload/src/sideload/application.rs | 57 +- isideload/src/sideload/builder.rs | 56 +- isideload/src/sideload/bundle.rs | 46 +- isideload/src/sideload/cert_identity.rs | 41 +- isideload/src/sideload/install.rs | 95 ++ isideload/src/sideload/mod.rs | 2 + isideload/src/sideload/sideloader.rs | 161 ++- 12 files changed, 2096 insertions(+), 101 deletions(-) create mode 100644 isideload/src/sideload/install.rs diff --git a/Cargo.lock b/Cargo.lock index bba36e8..7371123 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,6 +54,27 @@ dependencies = [ "subtle", ] +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -63,19 +84,217 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + [[package]] name = "anyhow" version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "apple-bundles" +version = "0.21.0" +dependencies = [ + "anyhow", + "plist", + "simple-file-manifest", + "walkdir", +] + +[[package]] +name = "apple-codesign" +version = "0.29.0" +dependencies = [ + "anyhow", + "apple-bundles", + "apple-flat-package", + "apple-xar", + "base64", + "bcder", + "bitflags", + "bytes", + "chrono", + "clap", + "cryptographic-message-syntax", + "der 0.7.10", + "dialoguer", + "difference", + "digest 0.10.7", + "dirs", + "elliptic-curve", + "env_logger", + "figment", + "filetime", + "glob", + "goblin", + "hex", + "log", + "md-5", + "minicbor", + "num-traits", + "object", + "oid-registry 0.7.1", + "once_cell", + "p12", + "p256", + "pem", + "pkcs1 0.7.5", + "pkcs8 0.10.2", + "plist", + "rand 0.8.5", + "rasn", + "rayon", + "regex", + "reqwest 0.12.28", + "ring", + "rsa 0.9.10", + "scroll", + "security-framework 2.11.1", + "security-framework-sys", + "semver", + "serde", + "serde_json", + "serde_yaml", + "sha2 0.10.9", + "signature 2.2.0", + "simple-file-manifest", + "spake2", + "spki 0.7.3", + "subtle", + "tempfile", + "thiserror 2.0.18", + "tokio", + "uuid", + "walkdir", + "widestring", + "windows-sys 0.59.0", + "x509", + "x509-certificate", + "xml-rs", + "yasna", + "zeroize", + "zip", + "zip_structs", +] + +[[package]] +name = "apple-flat-package" +version = "0.20.0" +dependencies = [ + "apple-xar", + "cpio-archive", + "flate2", + "scroll", + "serde", + "serde-xml-rs", + "thiserror 2.0.18", +] + +[[package]] +name = "apple-xar" +version = "0.20.0" +dependencies = [ + "base64", + "bcder", + "bzip2", + "chrono", + "cryptographic-message-syntax", + "digest 0.10.7", + "flate2", + "log", + "md-5", + "rand 0.8.5", + "reqwest 0.12.28", + "scroll", + "serde", + "serde-xml-rs", + "sha1", + "sha2 0.10.9", + "signature 2.2.0", + "thiserror 2.0.18", + "url", + "x509-certificate", + "xml-rs", + "xz2", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "asn1-rs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" +dependencies = [ + "asn1-rs-derive 0.5.1", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 1.0.69", +] + [[package]] name = "asn1-rs" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" dependencies = [ - "asn1-rs-derive", + "asn1-rs-derive 0.6.0", "asn1-rs-impl", "displaydoc", "nom", @@ -85,6 +304,18 @@ dependencies = [ "time", ] +[[package]] +name = "asn1-rs-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "asn1-rs-derive" version = "0.6.0" @@ -116,6 +347,7 @@ checksum = "d10e4f991a553474232bc0a31799f6d24b034a84c0971d80d2e2f78b2e576e40" dependencies = [ "compression-codecs", "compression-core", + "futures-io", "pin-project-lite", "tokio", ] @@ -131,6 +363,30 @@ dependencies = [ "syn", ] +[[package]] +name = "async_zip" +version = "0.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c50d65ce1b0e0cb65a785ff615f78860d7754290647d3b983208daa4f85e6" +dependencies = [ + "async-compression", + "crc32fast", + "futures-lite", + "pin-project", + "thiserror 2.0.18", + "tokio", + "tokio-util", +] + +[[package]] +name = "atomic" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" +dependencies = [ + "bytemuck", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -166,6 +422,12 @@ dependencies = [ "fs_extra", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base16ct" version = "1.0.0" @@ -200,6 +462,28 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "bitvec-nom2" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d988fcc40055ceaa85edc55875a08f8abd29018582647fd82ad6128dba14a5f0" +dependencies = [ + "bitvec", + "nom", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -242,6 +526,12 @@ version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + [[package]] name = "byteorder" version = "1.5.0" @@ -254,6 +544,32 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +[[package]] +name = "bytesize" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e93abca9e28e0a1b9877922aacb20576e05d4679ffa78c3d6dc22a26a216659" + +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.13+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "cbc" version = "0.1.2" @@ -320,7 +636,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" dependencies = [ "iana-time-zone", + "js-sys", "num-traits", + "serde", + "wasm-bindgen", "windows-link", ] @@ -345,6 +664,46 @@ dependencies = [ "inout 0.2.2", ] +[[package]] +name = "clap" +version = "4.5.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63be97961acde393029492ce0be7a1af7e323e6bae9511ebfac33751be5e6806" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f13174bda5dfd69d7e947827e5af4b0f2f94a4a3ee92912fba07a66150f21e2" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" + [[package]] name = "cmake" version = "0.1.57" @@ -372,6 +731,12 @@ dependencies = [ "x509-cert", ] +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + [[package]] name = "combine" version = "4.6.7" @@ -399,6 +764,19 @@ version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" +[[package]] +name = "console" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width", + "windows-sys 0.59.0", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -411,6 +789,24 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" +[[package]] +name = "const_panic" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e262cdaac42494e3ae34c43969f9cdeb7da178bdb4b66fa6a1ea2edb4c8ae652" +dependencies = [ + "typewit", +] + +[[package]] +name = "cookie-factory" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" +dependencies = [ + "futures", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -437,6 +833,16 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpio-archive" +version = "0.10.0" +dependencies = [ + "chrono", + "is_executable", + "simple-file-manifest", + "thiserror 2.0.18", +] + [[package]] name = "cpubits" version = "0.1.0" @@ -470,6 +876,43 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-bigint" version = "0.7.0-rc.25" @@ -512,11 +955,28 @@ version = "0.7.0-pre.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "334a79c97c0b7fa536716dc132fd417d0afbf471440a41fc25a5d9f66d8771cd" dependencies = [ - "crypto-bigint", + "crypto-bigint 0.7.0-rc.25", "libm", "rand_core 0.10.0", ] +[[package]] +name = "cryptographic-message-syntax" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c67889c5743b5987961460fbc999dbfa1bb4c7a8e5bfe37eb4d5e678ccb6d1" +dependencies = [ + "bcder", + "bytes", + "chrono", + "hex", + "pem", + "reqwest 0.12.28", + "ring", + "signature 2.2.0", + "x509-certificate", +] + [[package]] name = "ctr" version = "0.10.0-rc.3" @@ -535,6 +995,32 @@ dependencies = [ "cmov", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "fiat-crypto", + "rand_core 0.6.4", + "rustc_version", + "subtle", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "data-encoding" version = "2.10.0" @@ -592,7 +1078,7 @@ version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" dependencies = [ - "asn1-rs", + "asn1-rs 0.7.1", "displaydoc", "nom", "num-bigint", @@ -629,6 +1115,25 @@ dependencies = [ "cipher 0.4.4", ] +[[package]] +name = "dialoguer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" +dependencies = [ + "console", + "shell-words", + "tempfile", + "thiserror 1.0.69", + "zeroize", +] + +[[package]] +name = "difference" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" + [[package]] name = "digest" version = "0.10.7" @@ -636,6 +1141,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", + "const-oid 0.9.6", "crypto-common 0.1.7", "subtle", ] @@ -652,6 +1158,27 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -669,6 +1196,50 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der 0.7.10", + "digest 0.10.7", + "elliptic-curve", + "signature 2.2.0", + "spki 0.7.3", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.5", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "encoding_rs" version = "0.8.35" @@ -678,12 +1249,92 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_filter" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a1c3cc8e57274ec99de65301228b537f1e4eedc1b8e0f9411c6caac8ae7308f" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "figment" +version = "0.10.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" +dependencies = [ + "atomic", + "pear", + "serde", + "toml", + "uncased", + "version_check", +] + +[[package]] +name = "filetime" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +dependencies = [ + "cfg-if", + "libc", + "libredox", +] + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -734,6 +1385,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.31" @@ -782,6 +1439,19 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.31" @@ -831,6 +1501,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -883,6 +1554,34 @@ dependencies = [ "polyval", ] +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "goblin" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daa0a64d21a7eb230583b4c5f4e23b7e4e57974f96620f42a7e75e08ae66d745" +dependencies = [ + "log", + "plain", + "scroll", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "h2" version = "0.4.13" @@ -902,6 +1601,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + [[package]] name = "hashbrown" version = "0.15.5" @@ -929,6 +1637,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac 0.12.1", +] + [[package]] name = "hmac" version = "0.12.1" @@ -1027,10 +1744,12 @@ dependencies = [ "hyper", "hyper-util", "rustls", + "rustls-native-certs", "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", + "webpki-roots 1.0.6", ] [[package]] @@ -1175,7 +1894,9 @@ version = "0.1.52" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4031af51250d2f22f61a0d7fb7ea71ba8b6144b2b9dd3b7ee4a931fccbd1ec0" dependencies = [ + "async_zip", "base64", + "chrono", "futures", "plist", "plist-macro", @@ -1220,6 +1941,12 @@ dependencies = [ "serde_core", ] +[[package]] +name = "inlinable_string" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" + [[package]] name = "inout" version = "0.1.4" @@ -1256,12 +1983,28 @@ dependencies = [ "serde", ] +[[package]] +name = "is_executable" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baabb8b4867b26294d818bf3f651a454b6901431711abb96e296245888d6e8c4" +dependencies = [ + "windows-sys 0.60.2", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "isideload" version = "0.2.0" dependencies = [ "aes 0.9.0-rc.4", "aes-gcm", + "apple-codesign", "async-trait", "base64", "cbc 0.2.0-rc.3", @@ -1276,9 +2019,9 @@ dependencies = [ "plist-macro", "rand 0.10.0", "rcgen", - "reqwest", + "reqwest 0.13.2", "rootcause", - "rsa", + "rsa 0.10.0-rc.15", "serde", "serde_json", "sha2 0.11.0-rc.5", @@ -1292,12 +2035,45 @@ dependencies = [ "zip", ] +[[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.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +[[package]] +name = "jiff" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c867c356cc096b33f4981825ab281ecba3db0acefe60329f044c1789d94c6543" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7946b4325269738f270bb55b3c19ab5c5040525f83fd625259422a9d25d9be5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "jni" version = "0.21.1" @@ -1356,11 +2132,34 @@ dependencies = [ "zeroize", ] +[[package]] +name = "konst" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4381b9b00c55f251f2ebe9473aef7c117e96828def1a7cb3bd3f0f903c6894e9" +dependencies = [ + "const_panic", + "konst_kernel", + "typewit", +] + +[[package]] +name = "konst_kernel" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4b1eb7788f3824c629b1116a7a9060d6e898c358ebff59070093d51103dcc3c" +dependencies = [ + "typewit", +] + [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "leb128fmt" @@ -1389,6 +2188,17 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +[[package]] +name = "libredox" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +dependencies = [ + "bitflags", + "libc", + "redox_syscall", +] + [[package]] name = "linux-keyutils" version = "0.2.4" @@ -1399,6 +2209,12 @@ dependencies = [ "libc", ] +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + [[package]] name = "litemap" version = "0.8.1" @@ -1417,6 +2233,27 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "lzma-sys" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest 0.10.7", +] + [[package]] name = "memchr" version = "2.7.6" @@ -1429,6 +2266,26 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minicbor" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0452a60c1863c1f50b5f77cd295e8d2786849f35883f0b9e18e7e6e1b5691b0" +dependencies = [ + "minicbor-derive", +] + +[[package]] +name = "minicbor-derive" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd2209fff77f705b00c737016a48e73733d7fbccb8b007194db148f03561fb70" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "minimal" version = "0.1.0" @@ -1498,6 +2355,22 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +dependencies = [ + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + [[package]] name = "num-conv" version = "0.2.0" @@ -1513,6 +2386,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1520,6 +2404,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "crc32fast", + "flate2", + "hashbrown 0.15.5", + "indexmap", + "memchr", + "ruzstd", +] + +[[package]] +name = "oid-registry" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" +dependencies = [ + "asn1-rs 0.6.2", ] [[package]] @@ -1528,7 +2436,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" dependencies = [ - "asn1-rs", + "asn1-rs 0.7.1", ] [[package]] @@ -1537,12 +2445,41 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "openssl-probe" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "p12" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4873306de53fe82e7e484df31e1e947d61514b6ea2ed6cd7b45d63006fd9224" +dependencies = [ + "cbc 0.1.2", + "cipher 0.4.4", + "des", + "getrandom 0.2.17", + "hmac 0.12.1", + "lazy_static", + "rc2", + "sha1", + "yasna", +] + [[package]] name = "p12-keystore" version = "0.2.0" @@ -1566,6 +2503,23 @@ dependencies = [ "x509-parser 0.17.0", ] +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "pbkdf2" version = "0.12.2" @@ -1586,6 +2540,29 @@ dependencies = [ "hmac 0.13.0-rc.5", ] +[[package]] +name = "pear" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" +dependencies = [ + "inlinable_string", + "pear_codegen", + "yansi", +] + +[[package]] +name = "pear_codegen" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn", +] + [[package]] name = "pem" version = "3.0.6" @@ -1620,6 +2597,26 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1632,6 +2629,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der 0.7.10", + "pkcs8 0.10.2", + "spki 0.7.3", +] + [[package]] name = "pkcs1" version = "0.8.0-rc.4" @@ -1672,6 +2680,16 @@ dependencies = [ "spki 0.7.3", ] +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.10", + "spki 0.7.3", +] + [[package]] name = "pkcs8" version = "0.11.0-rc.10" @@ -1688,6 +2706,12 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + [[package]] name = "plist" version = "1.8.0" @@ -1721,6 +2745,21 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" +dependencies = [ + "portable-atomic", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -1755,6 +2794,15 @@ dependencies = [ "syn", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -1764,6 +2812,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "version_check", + "yansi", +] + [[package]] name = "quick-xml" version = "0.38.4" @@ -1844,13 +2905,30 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + [[package]] name = "rand" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ - "rand_chacha", + "rand_chacha 0.9.0", "rand_core 0.9.5", ] @@ -1865,6 +2943,16 @@ dependencies = [ "rand_core 0.10.0", ] +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_chacha" version = "0.9.0" @@ -1899,6 +2987,65 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" +[[package]] +name = "rasn" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e442690f86da40561d5548e7ffb4a18af90d1c1b3536090de847ca2d5a3a6426" +dependencies = [ + "arrayvec", + "bitvec", + "bitvec-nom2", + "bytes", + "chrono", + "either", + "hashbrown 0.14.5", + "konst", + "nom", + "num-bigint", + "num-integer", + "num-traits", + "once_cell", + "rasn-derive", + "serde_json", + "snafu", +] + +[[package]] +name = "rasn-derive" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0d374c7e4e985e6bc97ca7e7ad1d9642a8415db2017777d6e383002edaab2" +dependencies = [ + "either", + "itertools", + "proc-macro2", + "quote", + "rayon", + "syn", + "uuid", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "rc2" version = "0.8.1" @@ -1922,6 +3069,97 @@ dependencies = [ "yasna", ] +[[package]] +name = "redox_syscall" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35985aa610addc02e24fc232012c86fd11f14111180f902b67e2d5331f8ebf2b" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots 1.0.6", +] + [[package]] name = "reqwest" version = "0.13.2" @@ -1998,6 +3236,26 @@ dependencies = [ "triomphe", ] +[[package]] +name = "rsa" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" +dependencies = [ + "const-oid 0.9.6", + "digest 0.10.7", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1 0.7.5", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "signature 2.2.0", + "spki 0.7.3", + "subtle", + "zeroize", +] + [[package]] name = "rsa" version = "0.10.0-rc.15" @@ -2005,11 +3263,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b342b99544549f37509ed7fd42b0cea04bfd9ce07c16ca56094cf0fbeefbbcd" dependencies = [ "const-oid 0.10.2", - "crypto-bigint", + "crypto-bigint 0.7.0-rc.25", "crypto-primes", "digest 0.11.0-rc.11", - "pkcs1", - "pkcs8", + "pkcs1 0.8.0-rc.4", + "pkcs8 0.11.0-rc.10", "rand_core 0.10.0", "signature 3.0.0-rc.10", "spki 0.8.0-rc.4", @@ -2022,6 +3280,15 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rusticata-macros" version = "4.1.0" @@ -2031,6 +3298,19 @@ dependencies = [ "nom", ] +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + [[package]] name = "rustls" version = "0.23.36" @@ -2039,6 +3319,7 @@ checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" dependencies = [ "aws-lc-rs", "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -2110,6 +3391,21 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "ruzstd" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad02996bfc73da3e301efe90b1837be9ed8f4a462b6ed410aa35d00381de89f" +dependencies = [ + "twox-hash", +] + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + [[package]] name = "salsa20" version = "0.10.2" @@ -2137,6 +3433,26 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "scroll" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "scrypt" version = "0.11.0" @@ -2148,6 +3464,20 @@ dependencies = [ "sha2 0.10.9", ] +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.10", + "generic-array", + "pkcs8 0.10.2", + "subtle", + "zeroize", +] + [[package]] name = "security-framework" version = "2.11.1" @@ -2200,6 +3530,18 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-xml-rs" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb3aa78ecda1ebc9ec9847d5d3aba7d618823446a049ba2491940506da6e2782" +dependencies = [ + "log", + "serde", + "thiserror 1.0.69", + "xml-rs", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -2233,13 +3575,47 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "serdect" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9af4a3e75ebd5599b30d4de5768e00b5095d518a79fefc3ecbaf77e665d1ec06" dependencies = [ - "base16ct", + "base16ct 1.0.0", "serde", ] @@ -2285,6 +3661,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shell-words" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77" + [[package]] name = "shlex" version = "1.3.0" @@ -2297,6 +3679,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ + "digest 0.10.7", "rand_core 0.6.4", ] @@ -2316,6 +3699,12 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +[[package]] +name = "simple-file-manifest" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd19be0257552dd56d1bb6946f89f193c6e5b9f13cc9327c4bc84a357507c74" + [[package]] name = "slab" version = "0.4.12" @@ -2328,6 +3717,27 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "snafu" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e84b3f4eacbf3a1ce05eac6763b4d629d60cbc94d632e4092c54ade71f1e1a2" +dependencies = [ + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1c97747dbf44bb1ca44a561ece23508e99cb592e862f22222dcf42f51d1e451" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "socket2" version = "0.6.2" @@ -2338,6 +3748,24 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "spake2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5482afe85a0b6ce956c945401598dbc527593c77ba51d0a87a586938b1b893a" +dependencies = [ + "curve25519-dalek", + "hkdf", + "rand_core 0.6.4", + "sha2 0.10.9", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "spki" version = "0.7.3" @@ -2364,7 +3792,7 @@ version = "0.7.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0b7d0b0d27b2475e779315c09af698ac9f6d40016bc011bf3fa0f0054ce38ed" dependencies = [ - "crypto-bigint", + "crypto-bigint 0.7.0-rc.25", "crypto-common 0.2.0", "digest 0.11.0-rc.11", "subtle", @@ -2376,6 +3804,18 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "subtle" version = "2.6.1" @@ -2434,6 +3874,25 @@ dependencies = [ "libc", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" +dependencies = [ + "fastrand", + "getrandom 0.4.1", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -2599,11 +4058,53 @@ checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "tower" version = "0.5.3" @@ -2742,6 +4243,16 @@ dependencies = [ "utf-8", ] +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "static_assertions", +] + [[package]] name = "typed-path" version = "0.12.2" @@ -2754,12 +4265,42 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "typewit" +version = "1.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c1ae7cc0fdb8b842d65d127cb981574b0d2b249b74d1c7a2986863dc134f71" +dependencies = [ + "typewit_proc_macros", +] + +[[package]] +name = "typewit_proc_macros" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6" + +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-ident" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -2776,6 +4317,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "untrusted" version = "0.7.1" @@ -2812,6 +4359,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.20.0" @@ -3018,6 +4571,12 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "widestring" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" + [[package]] name = "winapi-util" version = "0.1.11" @@ -3106,6 +4665,15 @@ dependencies = [ "windows-targets 0.42.2", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -3157,6 +4725,21 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -3196,6 +4779,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -3214,6 +4803,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -3232,6 +4827,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -3262,6 +4863,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -3280,6 +4887,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -3298,6 +4911,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -3316,6 +4935,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -3328,6 +4953,15 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen" version = "0.51.0" @@ -3422,6 +5056,25 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x509" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3cec94c3999f31341553f358ef55f65fc031291a022cd42ec0ce7219560c76" +dependencies = [ + "chrono", + "cookie-factory", +] + [[package]] name = "x509-cert" version = "0.2.5" @@ -3458,12 +5111,12 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460" dependencies = [ - "asn1-rs", + "asn1-rs 0.7.1", "data-encoding", "der-parser", "lazy_static", "nom", - "oid-registry", + "oid-registry 0.8.1", "rusticata-macros", "thiserror 2.0.18", "time", @@ -3475,18 +5128,39 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202" dependencies = [ - "asn1-rs", + "asn1-rs 0.7.1", "aws-lc-rs", "data-encoding", "der-parser", "lazy_static", "nom", - "oid-registry", + "oid-registry 0.8.1", "rusticata-macros", "thiserror 2.0.18", "time", ] +[[package]] +name = "xml-rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" + +[[package]] +name = "xz2" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +dependencies = [ + "lzma-sys", +] + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "yasna" version = "0.5.2" @@ -3627,6 +5301,17 @@ dependencies = [ "zopfli", ] +[[package]] +name = "zip_structs" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce824a6bfffe8942820fa36d24973b7c83a40896749a42e33de0abdd11750ee5" +dependencies = [ + "byteorder", + "bytesize", + "thiserror 1.0.69", +] + [[package]] name = "zlib-rs" version = "0.6.0" diff --git a/README.md b/README.md index 77069b0..520cad3 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,11 @@ This branch is home to isideload-next, the next major version of isideload. It f Things left todo before the rewrite is considered finished -- Download provisioning profiles -- Signing apps -- Installing apps -(will superceed the original isideload at this point) -- Remove dependency on ring -- More parallelism/cachng for better performance +- Proper entitlement handling + - actually parse macho files and stuff, right now it just uses the bare minimum and applies extra entitlements for livecontainer +- Remove dependency on ring and reduce duplicate dependencies + - partially just need to wait for the rust crypto ecosystem to get through another release cycle +- More parallelism/caching for better performance ## Licensing diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 6d740de..a38ff98 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -19,7 +19,7 @@ use tracing_subscriber::FmtSubscriber; async fn main() { isideload::init().expect("Failed to initialize error reporting"); let subscriber = FmtSubscriber::builder() - .with_max_level(Level::INFO) + .with_max_level(Level::DEBUG) .finish(); tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 5cb6fc9..28433ac 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -19,7 +19,7 @@ keyring-storage = ["keyring"] # Once that becomes stable, hopefuly duplicate dependencies should clean up.\ # Until then, I will wince in pain every time I see how long the output of cargo tree -d is. [dependencies] -idevice = { version = "0.1.52", optional = true } +idevice = { version = "0.1.52", optional = true, features = ["afc", "installation_proxy"]} plist = "1.8" plist-macro = "0.1.4" reqwest = { version = "0.13.2", features = ["json", "gzip"] } @@ -49,4 +49,5 @@ keyring = { version = "3.6.3", features = ["apple-native", "linux-native-sync-pe x509-certificate = "0.25" rcgen = { version = "0.14.7", default-features = false, features = ["aws_lc_rs", "pem"] } p12-keystore = "0.2.0" -zip = { version = "7.4", default-features = false, features = ["deflate"] } \ No newline at end of file +zip = { version = "7.4", default-features = false, features = ["deflate"] } +apple-codesign = { path = "../../plume-apple-platform-rs/apple-codesign", default-features = false} diff --git a/isideload/src/lib.rs b/isideload/src/lib.rs index 13d7121..be251c1 100644 --- a/isideload/src/lib.rs +++ b/isideload/src/lib.rs @@ -1,3 +1,4 @@ +use idevice::IdeviceError; use rootcause::{ hooks::{Hooks, context_formatter::ContextFormatterHook}, prelude::*, @@ -25,6 +26,9 @@ pub enum SideloadError { #[error("Invalid bundle: {0}")] InvalidBundle(String), + + #[error(transparent)] + IdeviceError(#[from] IdeviceError), } // The default reqwest error formatter sucks and provides no info diff --git a/isideload/src/sideload/application.rs b/isideload/src/sideload/application.rs index 692d37f..8cf531c 100644 --- a/isideload/src/sideload/application.rs +++ b/isideload/src/sideload/application.rs @@ -5,7 +5,6 @@ use crate::SideloadError; use crate::dev::app_ids::{AppId, AppIdsApi}; use crate::dev::developer_session::DeveloperSession; use crate::dev::teams::DeveloperTeam; -use crate::sideload::builder::ExtensionsBehavior; use crate::sideload::bundle::Bundle; use crate::sideload::cert_identity::CertificateIdentity; use rootcause::option_ext::OptionExt; @@ -13,6 +12,7 @@ use rootcause::prelude::*; use std::fs::File; use std::path::PathBuf; use tokio::io::AsyncWriteExt; +use tracing::info; use zip::ZipArchive; pub struct Application { @@ -81,7 +81,8 @@ impl Application { } pub fn get_special_app(&self) -> Option { - let special_app = match self.bundle.bundle_identifier().unwrap_or("") { + let bundle_id = self.bundle.bundle_identifier().unwrap_or(""); + let special_app = match bundle_id { "com.rileytestut.AltStore" => Some(SpecialApp::AltStore), "com.SideStore.SideStore" => Some(SpecialApp::SideStore), _ => None, @@ -99,6 +100,10 @@ impl Application { return Some(SpecialApp::SideStoreLc); } + if bundle_id == "com.kdt.livecontainer" { + return Some(SpecialApp::LiveContainer); + } + None } @@ -154,7 +159,7 @@ impl Application { pub async fn register_app_ids( &self, - mode: &ExtensionsBehavior, + //mode: &ExtensionsBehavior, dev_session: &mut DeveloperSession, team: &DeveloperTeam, ) -> Result, Report> { @@ -166,19 +171,16 @@ impl Application { .list_app_ids(&team, None) .await .context("Failed to list app IDs for the developer team")?; - let app_ids_to_register = match mode { - ExtensionsBehavior::RegisterAll => bundles_with_app_id - .iter() - .filter(|bundle| { - let bundle_id = bundle.bundle_identifier().unwrap_or(""); - !list_app_ids_response - .app_ids - .iter() - .any(|app_id| app_id.identifier == bundle_id) - }) - .collect::>(), - _ => todo!(), - }; + let app_ids_to_register = bundles_with_app_id + .iter() + .filter(|bundle| { + let bundle_id = bundle.bundle_identifier().unwrap_or(""); + !list_app_ids_response + .app_ids + .iter() + .any(|app_id| app_id.identifier == bundle_id) + }) + .collect::>(); if let Some(available) = list_app_ids_response.available_quantity && app_ids_to_register.len() > available.try_into().unwrap() @@ -220,10 +222,11 @@ impl Application { } let special = special.as_ref().unwrap(); - if special == &SpecialApp::SideStoreLc - || special == &SpecialApp::SideStore - || special == &SpecialApp::AltStore - { + if matches!( + special, + SpecialApp::SideStoreLc | SpecialApp::SideStore | SpecialApp::AltStore + ) { + info!("Injecting certificate for {}", special); self.bundle.app_info.insert( "ALTAppGroups".to_string(), plist::Value::Array(vec![plist::Value::String(group_identifier.to_string())]), @@ -265,6 +268,20 @@ impl Application { pub enum SpecialApp { SideStore, SideStoreLc, + LiveContainer, AltStore, StikStore, } + +// impl display +impl std::fmt::Display for SpecialApp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SpecialApp::SideStore => write!(f, "SideStore"), + SpecialApp::SideStoreLc => write!(f, "SideStore+LiveContainer"), + SpecialApp::LiveContainer => write!(f, "LiveContainer"), + SpecialApp::AltStore => write!(f, "AltStore"), + SpecialApp::StikStore => write!(f, "StikStore"), + } + } +} diff --git a/isideload/src/sideload/builder.rs b/isideload/src/sideload/builder.rs index fa70169..096601e 100644 --- a/isideload/src/sideload/builder.rs +++ b/isideload/src/sideload/builder.rs @@ -49,34 +49,34 @@ pub enum ExtensionsBehaviorChoice { RemoveExtensions, } -/// Behavior used when an app contains sub bundles -pub enum ExtensionsBehavior { - /// Use the main app id/profile for all sub-bundles - ReuseMain, - /// Create separate app ids/profiles for each sub-bundle - RegisterAll, - /// Remove all sub-bundles - RemoveExtensions, - /// Prompt the user to choose one of the above behaviors - Prompt(fn(&Vec) -> ExtensionsBehaviorChoice), -} +// /// Behavior used when an app contains sub bundles +// pub enum ExtensionsBehavior { +// /// Use the main app id/profile for all sub-bundles +// ReuseMain, +// /// Create separate app ids/profiles for each sub-bundle +// RegisterAll, +// /// Remove all sub-bundles +// RemoveExtensions, +// /// Prompt the user to choose one of the above behaviors +// Prompt(fn(&Vec) -> ExtensionsBehaviorChoice), +// } -impl From for ExtensionsBehavior { - fn from(choice: ExtensionsBehaviorChoice) -> Self { - match choice { - ExtensionsBehaviorChoice::ReuseMain => ExtensionsBehavior::ReuseMain, - ExtensionsBehaviorChoice::RegisterAll => ExtensionsBehavior::RegisterAll, - ExtensionsBehaviorChoice::RemoveExtensions => ExtensionsBehavior::RemoveExtensions, - } - } -} +// impl From for ExtensionsBehavior { +// fn from(choice: ExtensionsBehaviorChoice) -> Self { +// match choice { +// ExtensionsBehaviorChoice::ReuseMain => ExtensionsBehavior::ReuseMain, +// ExtensionsBehaviorChoice::RegisterAll => ExtensionsBehavior::RegisterAll, +// ExtensionsBehaviorChoice::RemoveExtensions => ExtensionsBehavior::RemoveExtensions, +// } +// } +// } pub struct SideloaderBuilder { developer_session: DeveloperSession, apple_email: String, team_selection: Option, max_certs_behavior: Option, - extensions_behavior: Option, + //extensions_behavior: Option, storage: Option>, machine_name: Option, } @@ -90,7 +90,7 @@ impl SideloaderBuilder { machine_name: None, apple_email, max_certs_behavior: None, - extensions_behavior: None, + // extensions_behavior: None, } } @@ -128,10 +128,10 @@ impl SideloaderBuilder { self } - pub fn extensions_behavior(mut self, behavior: ExtensionsBehavior) -> Self { - self.extensions_behavior = Some(behavior); - self - } + // pub fn extensions_behavior(mut self, behavior: ExtensionsBehavior) -> Self { + // self.extensions_behavior = Some(behavior); + // self + // } /// Build the `Sideloader` instance with the provided configuration pub fn build(self) -> Sideloader { @@ -143,8 +143,8 @@ impl SideloaderBuilder { self.machine_name.unwrap_or_else(|| "isideload".to_string()), self.storage .unwrap_or_else(|| Box::new(crate::util::storage::new_storage())), - self.extensions_behavior - .unwrap_or(ExtensionsBehavior::RegisterAll), + // self.extensions_behavior + // .unwrap_or(ExtensionsBehavior::RegisterAll), ) } } diff --git a/isideload/src/sideload/bundle.rs b/isideload/src/sideload/bundle.rs index 62f9732..fdee936 100644 --- a/isideload/src/sideload/bundle.rs +++ b/isideload/src/sideload/bundle.rs @@ -10,7 +10,7 @@ use std::{ use crate::SideloadError; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Bundle { pub app_info: Dictionary, pub bundle_dir: PathBuf, @@ -134,6 +134,50 @@ impl Bundle { )?; Ok(()) } + + fn from_dylib_path(dylib_path: PathBuf) -> Self { + Self { + app_info: Dictionary::new(), + bundle_dir: dylib_path, + app_extensions: Vec::new(), + frameworks: Vec::new(), + _libraries: Vec::new(), + } + } + + fn collect_dylib_bundles(&self) -> Vec { + self._libraries + .iter() + .map(|relative| Self::from_dylib_path(self.bundle_dir.join(relative))) + .collect() + } + + fn collect_nested_bundles_into(&self, bundles: &mut Vec) { + for bundle in &self.app_extensions { + bundles.push(bundle.clone()); + bundle.collect_nested_bundles_into(bundles); + } + + for bundle in &self.frameworks { + bundles.push(bundle.clone()); + bundle.collect_nested_bundles_into(bundles); + } + } + + pub fn collect_nested_bundles(&self) -> Vec { + let mut bundles = Vec::new(); + self.collect_nested_bundles_into(&mut bundles); + bundles.extend(self.collect_dylib_bundles()); + bundles + } + + pub fn collect_bundles_sorted(&self) -> Vec { + let mut bundles = self.collect_nested_bundles(); + bundles.push(self.clone()); + bundles.sort_by_key(|b| b.bundle_dir.components().count()); + bundles.reverse(); + bundles + } } fn assert_bundle(condition: bool, msg: &str) -> Result<(), Report> { diff --git a/isideload/src/sideload/cert_identity.rs b/isideload/src/sideload/cert_identity.rs index 58b605d..c8c80be 100644 --- a/isideload/src/sideload/cert_identity.rs +++ b/isideload/src/sideload/cert_identity.rs @@ -1,3 +1,7 @@ +use apple_codesign::{ + SigningSettings, + cryptography::{InMemoryPrivateKey, PrivateKey}, +}; use hex::ToHex; use rcgen::{CertificateParams, DistinguishedName, DnType, KeyPair, PKCS_RSA_SHA256}; use rootcause::prelude::*; @@ -9,7 +13,7 @@ use rsa::{ use sha2::{Digest, Sha256}; use tracing::{error, info}; -use x509_certificate::X509Certificate; +use x509_certificate::CapturedX509Certificate; use crate::{ SideloadError, @@ -25,8 +29,9 @@ use crate::{ pub struct CertificateIdentity { pub machine_id: String, pub machine_name: String, - pub certificate: X509Certificate, + pub certificate: CapturedX509Certificate, pub private_key: RsaPrivateKey, + pub signing_key: InMemoryPrivateKey, } impl CertificateIdentity { @@ -63,7 +68,7 @@ impl CertificateIdentity { pub fn get_serial_number(&self) -> String { let serial: String = self.certificate.serial_number_asn1().encode_hex(); - serial.trim_start_matches('0').to_string() + serial.trim_start_matches('0').to_string().to_uppercase() } pub async fn retrieve( @@ -75,6 +80,7 @@ impl CertificateIdentity { max_certs_behavior: &MaxCertsBehavior, ) -> Result { let pr = Self::retrieve_private_key(apple_email, storage).await?; + let signing_key = Self::build_signing_key(&pr)?; let found = Self::find_matching(&pr, machine_name, developer_session, team).await; if let Ok(Some((cert, x509_cert))) = found { @@ -84,6 +90,7 @@ impl CertificateIdentity { machine_name: cert.machine_name.clone().unwrap_or_default(), certificate: x509_cert, private_key: pr, + signing_key, }); } @@ -107,6 +114,7 @@ impl CertificateIdentity { machine_name: cert.machine_name.clone().unwrap_or_default(), certificate: x509_cert, private_key: pr, + signing_key, }) } @@ -139,7 +147,7 @@ impl CertificateIdentity { machine_name: &str, developer_session: &mut DeveloperSession, team: &DeveloperTeam, - ) -> Result, Report> { + ) -> Result, Report> { let public_key_der = private_key .to_public_key() .to_pkcs1_der()? @@ -156,7 +164,7 @@ impl CertificateIdentity { }) { let x509_cert = - X509Certificate::from_der(cert.cert_content.as_ref().unwrap().as_ref())?; + CapturedX509Certificate::from_der(cert.cert_content.as_ref().unwrap().as_ref())?; if public_key_der == x509_cert.public_key_data().as_ref() { return Ok(Some((cert.clone(), x509_cert))); @@ -172,7 +180,7 @@ impl CertificateIdentity { developer_session: &mut DeveloperSession, team: &DeveloperTeam, max_certs_behavior: &MaxCertsBehavior, - ) -> Result<(DevelopmentCertificate, X509Certificate), Report> { + ) -> Result<(DevelopmentCertificate, CapturedX509Certificate), Report> { let csr = Self::build_csr(private_key).context("Failed to generate CSR")?; let mut i = 0; @@ -196,7 +204,7 @@ impl CertificateIdentity { report!("Failed to find certificate after submitting CSR") })?; - let x509_cert = X509Certificate::from_der( + let x509_cert = CapturedX509Certificate::from_der( apple_cert .cert_content .as_ref() @@ -264,6 +272,11 @@ impl CertificateIdentity { Ok(params.serialize_request(&subject_key)?.pem()?) } + fn build_signing_key(private_key: &RsaPrivateKey) -> Result { + let pkcs8 = private_key.to_pkcs8_der()?; + Ok(InMemoryPrivateKey::from_pkcs8_der(pkcs8.as_bytes())?) + } + async fn revoke_others( developer_session: &mut DeveloperSession, team: &DeveloperTeam, @@ -310,4 +323,18 @@ impl CertificateIdentity { } } } + + pub fn setup_signing_settings<'a>( + &'a self, + settings: &mut SigningSettings<'a>, + ) -> Result<(), Report> { + settings.set_signing_key( + self.signing_key.as_key_info_signer(), + self.certificate.clone(), + ); + settings.chain_apple_certificates(); + settings.set_team_id_from_signing_certificate(); + + Ok(()) + } } diff --git a/isideload/src/sideload/install.rs b/isideload/src/sideload/install.rs new file mode 100644 index 0000000..a9557d0 --- /dev/null +++ b/isideload/src/sideload/install.rs @@ -0,0 +1,95 @@ +use idevice::{ + IdeviceService, afc::AfcClient, installation_proxy::InstallationProxyClient, + provider::IdeviceProvider, +}; +use plist_macro::plist; +use rootcause::prelude::*; + +use crate::SideloadError as Error; +use std::pin::Pin; +use std::{future::Future, path::Path}; + +/// Installs an ***already signed*** app onto your device. +/// To sign and install an app, see [`crate::sideload::sideload_app`] +pub async fn install_app( + provider: &impl IdeviceProvider, + app_path: &Path, + progress_callback: impl Fn(u64), +) -> Result<(), Report> { + let mut afc_client = AfcClient::connect(provider) + .await + .map_err(Error::IdeviceError)?; + + let dir = format!( + "PublicStaging/{}", + app_path.file_name().unwrap().to_string_lossy() + ); + afc_upload_dir(&mut afc_client, app_path, &dir).await?; + + let mut instproxy_client = InstallationProxyClient::connect(provider) + .await + .map_err(Error::IdeviceError)?; + + let options = plist!(dict { + "PackageType": "Developer" + }); + + instproxy_client + .install_with_callback( + dir, + Some(plist::Value::Dictionary(options)), + async |(percentage, _)| { + progress_callback(percentage); + }, + (), + ) + .await + .map_err(Error::IdeviceError)?; + + Ok(()) +} + +fn afc_upload_dir<'a>( + afc_client: &'a mut AfcClient, + path: &'a Path, + afc_path: &'a str, +) -> Pin> + Send + 'a>> { + Box::pin(async move { + let entries = std::fs::read_dir(path)?; + afc_client + .mk_dir(afc_path) + .await + .map_err(Error::IdeviceError)?; + for entry in entries { + let entry = entry?; + let path = entry.path(); + if path.is_dir() { + let new_afc_path = format!( + "{}/{}", + afc_path, + path.file_name().unwrap().to_string_lossy() + ); + afc_upload_dir(afc_client, &path, &new_afc_path).await?; + } else { + let mut file_handle = afc_client + .open( + format!( + "{}/{}", + afc_path, + path.file_name().unwrap().to_string_lossy() + ), + idevice::afc::opcode::AfcFopenMode::WrOnly, + ) + .await + .map_err(Error::IdeviceError)?; + let bytes = std::fs::read(&path)?; + file_handle + .write_entire(&bytes) + .await + .map_err(Error::IdeviceError)?; + file_handle.close().await.map_err(Error::IdeviceError)?; + } + } + Ok(()) + }) +} diff --git a/isideload/src/sideload/mod.rs b/isideload/src/sideload/mod.rs index c194dc1..d0258ed 100644 --- a/isideload/src/sideload/mod.rs +++ b/isideload/src/sideload/mod.rs @@ -2,5 +2,7 @@ pub mod application; pub mod builder; pub mod bundle; pub mod cert_identity; +#[cfg(feature = "install")] +pub mod install; pub mod sideloader; pub use builder::{SideloaderBuilder, TeamSelection}; diff --git a/isideload/src/sideload/sideloader.rs b/isideload/src/sideload/sideloader.rs index f8ded2a..377ecf8 100644 --- a/isideload/src/sideload/sideloader.rs +++ b/isideload/src/sideload/sideloader.rs @@ -9,16 +9,19 @@ use crate::{ sideload::{ TeamSelection, application::{Application, SpecialApp}, - builder::{ExtensionsBehavior, MaxCertsBehavior}, + builder::MaxCertsBehavior, cert_identity::CertificateIdentity, }, - util::{device::IdeviceInfo, storage::SideloadingStorage}, + util::{device::IdeviceInfo, plist::PlistDataExtract, storage::SideloadingStorage}, }; use std::path::PathBuf; +use apple_codesign::{SigningSettings, UnifiedSigner}; use idevice::provider::IdeviceProvider; -use rootcause::prelude::*; +use plist::Dictionary; +use plist_macro::plist_to_xml_string; +use rootcause::{option_ext::OptionExt, prelude::*}; use tracing::info; pub struct Sideloader { @@ -28,7 +31,7 @@ pub struct Sideloader { machine_name: String, apple_email: String, max_certs_behavior: MaxCertsBehavior, - extensions_behavior: ExtensionsBehavior, + //extensions_behavior: ExtensionsBehavior, } impl Sideloader { @@ -42,7 +45,7 @@ impl Sideloader { max_certs_behavior: MaxCertsBehavior, machine_name: String, storage: Box, - extensions_behavior: ExtensionsBehavior, + //extensions_behavior: ExtensionsBehavior, ) -> Self { Sideloader { team_selection, @@ -51,24 +54,20 @@ impl Sideloader { machine_name, apple_email, max_certs_behavior, - extensions_behavior, + //extensions_behavior, } } /// Sign and install an app - pub async fn install_app( + pub async fn sign_app( &mut self, - device_provider: &impl IdeviceProvider, app_path: PathBuf, - ) -> Result<(), Report> { - let device_info = IdeviceInfo::from_device(device_provider).await?; - - let team = self.get_team().await?; - - self.dev_session - .ensure_device_registered(&team, &device_info.name, &device_info.udid, None) - .await?; - + team: Option, + ) -> Result { + let team = match team { + Some(t) => t, + None => self.get_team().await?, + }; let cert_identity = CertificateIdentity::retrieve( &self.machine_name, &self.apple_email, @@ -87,7 +86,10 @@ impl Sideloader { let main_app_id_str = format!("{}.{}", main_bundle_id, team.team_id); app.update_bundle_id(&main_bundle_id, &main_app_id_str)?; let mut app_ids = app - .register_app_ids(&self.extensions_behavior, &mut self.dev_session, &team) + .register_app_ids( + /*&self.extensions_behavior, */ &mut self.dev_session, + &team, + ) .await?; let main_app_id = match app_ids .iter() @@ -130,7 +132,8 @@ impl Sideloader { } app.apply_special_app_behavior(&special, &group_identifier, &cert_identity) - .await?; + .await + .context("Failed to modify app bundle")?; let provisioning_profile = self .dev_session @@ -145,9 +148,67 @@ impl Sideloader { ext.write_info()?; } - Ok(()) + tokio::fs::write( + app.bundle.bundle_dir.join("embedded.mobileprovision"), + provisioning_profile.encoded_profile.as_ref(), + ) + .await?; + + let mut settings = Self::signing_settings(&cert_identity)?; + let entitlements: Dictionary = Self::entitlements_from_prov( + provisioning_profile.encoded_profile.as_ref(), + &special, + &team, + )?; + + settings + .set_entitlements_xml( + apple_codesign::SettingsScope::Main, + plist_to_xml_string(&entitlements), + ) + .context("Failed to set entitlements XML")?; + let signer = UnifiedSigner::new(settings); + + for bundle in app.bundle.collect_bundles_sorted() { + info!("Signing bundle {}", bundle.bundle_dir.display()); + signer + .sign_path_in_place(&bundle.bundle_dir) + .context(format!( + "Failed to sign bundle: {}", + bundle.bundle_dir.display() + ))?; + } + + info!("App signed!"); + + Ok(app.bundle.bundle_dir.clone()) } + #[cfg(feature = "install")] + pub async fn install_app( + &mut self, + device_provider: &impl IdeviceProvider, + app_path: PathBuf, + ) -> Result<(), Report> { + let device_info = IdeviceInfo::from_device(device_provider).await?; + + let team = self.get_team().await?; + self.dev_session + .ensure_device_registered(&team, &device_info.name, &device_info.udid, None) + .await?; + + let signed_app_path = self.sign_app(app_path, Some(team)).await?; + + info!("Installing..."); + + crate::sideload::install::install_app(device_provider, &signed_app_path, |progress| { + info!("Installing: {}%", progress); + }) + .await + .context("Failed to install app on device")?; + + Ok(()) + } /// Get the developer team according to the configured team selection behavior pub async fn get_team(&mut self) -> Result { let teams = self.dev_session.list_teams().await?; @@ -175,4 +236,64 @@ impl Sideloader { } }) } + + pub fn signing_settings<'a>( + cert: &'a CertificateIdentity, + ) -> Result, Report> { + let mut settings = SigningSettings::default(); + + cert.setup_signing_settings(&mut settings)?; + settings.set_for_notarization(false); + settings.set_shallow(true); + + Ok(settings) + } + + fn entitlements_from_prov( + data: &[u8], + special: &Option, + team: &DeveloperTeam, + ) -> Result { + let start = data + .windows(6) + .position(|w| w == b"") + .ok_or_report()? + + 8; + let plist_data = &data[start..end]; + let plist = plist::Value::from_reader_xml(plist_data)?; + + let mut entitlements = plist + .as_dictionary() + .ok_or_report()? + .get_dict("Entitlements")? + .clone(); + + if matches!( + special, + Some(SpecialApp::SideStoreLc) | Some(SpecialApp::LiveContainer) + ) { + let mut keychain_access = vec![plist::Value::String(format!( + "{}.com.kdt.livecontainer.shared", + team.team_id + ))]; + + for number in 1..128 { + keychain_access.push(plist::Value::String(format!( + "{}.com.kdt.livecontainer.shared.{}", + team.team_id, number + ))); + } + + entitlements.insert( + "keychain-access-groups".to_string(), + plist::Value::Array(keychain_access), + ); + } + + Ok(entitlements) + } } From 4c4a5d0051ba695700e105e19a8731c8b48cef1d Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 14 Feb 2026 12:41:00 -0500 Subject: [PATCH 60/71] Switch to crates.io version of isideload-apple-codesign --- Cargo.lock | 651 +++++++++++++---------------------- README.md | 11 +- examples/minimal/src/main.rs | 2 +- isideload/Cargo.toml | 5 +- 4 files changed, 255 insertions(+), 414 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7371123..4aee0f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,13 +136,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" [[package]] name = "apple-bundles" version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f40bb8f844cec39fa3aceae717808c2ac3d2b6c474a9dffbeba07a4a945d10" dependencies = [ "anyhow", "plist", @@ -150,151 +152,19 @@ dependencies = [ "walkdir", ] -[[package]] -name = "apple-codesign" -version = "0.29.0" -dependencies = [ - "anyhow", - "apple-bundles", - "apple-flat-package", - "apple-xar", - "base64", - "bcder", - "bitflags", - "bytes", - "chrono", - "clap", - "cryptographic-message-syntax", - "der 0.7.10", - "dialoguer", - "difference", - "digest 0.10.7", - "dirs", - "elliptic-curve", - "env_logger", - "figment", - "filetime", - "glob", - "goblin", - "hex", - "log", - "md-5", - "minicbor", - "num-traits", - "object", - "oid-registry 0.7.1", - "once_cell", - "p12", - "p256", - "pem", - "pkcs1 0.7.5", - "pkcs8 0.10.2", - "plist", - "rand 0.8.5", - "rasn", - "rayon", - "regex", - "reqwest 0.12.28", - "ring", - "rsa 0.9.10", - "scroll", - "security-framework 2.11.1", - "security-framework-sys", - "semver", - "serde", - "serde_json", - "serde_yaml", - "sha2 0.10.9", - "signature 2.2.0", - "simple-file-manifest", - "spake2", - "spki 0.7.3", - "subtle", - "tempfile", - "thiserror 2.0.18", - "tokio", - "uuid", - "walkdir", - "widestring", - "windows-sys 0.59.0", - "x509", - "x509-certificate", - "xml-rs", - "yasna", - "zeroize", - "zip", - "zip_structs", -] - -[[package]] -name = "apple-flat-package" -version = "0.20.0" -dependencies = [ - "apple-xar", - "cpio-archive", - "flate2", - "scroll", - "serde", - "serde-xml-rs", - "thiserror 2.0.18", -] - -[[package]] -name = "apple-xar" -version = "0.20.0" -dependencies = [ - "base64", - "bcder", - "bzip2", - "chrono", - "cryptographic-message-syntax", - "digest 0.10.7", - "flate2", - "log", - "md-5", - "rand 0.8.5", - "reqwest 0.12.28", - "scroll", - "serde", - "serde-xml-rs", - "sha1", - "sha2 0.10.9", - "signature 2.2.0", - "thiserror 2.0.18", - "url", - "x509-certificate", - "xml-rs", - "xz2", -] - [[package]] name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -[[package]] -name = "asn1-rs" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" -dependencies = [ - "asn1-rs-derive 0.5.1", - "asn1-rs-impl", - "displaydoc", - "nom", - "num-traits", - "rusticata-macros", - "thiserror 1.0.69", -] - [[package]] name = "asn1-rs" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" dependencies = [ - "asn1-rs-derive 0.6.0", + "asn1-rs-derive", "asn1-rs-impl", "displaydoc", "nom", @@ -304,18 +174,6 @@ dependencies = [ "time", ] -[[package]] -name = "asn1-rs-derive" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "asn1-rs-derive" version = "0.6.0" @@ -341,9 +199,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.37" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d10e4f991a553474232bc0a31799f6d24b034a84c0971d80d2e2f78b2e576e40" +checksum = "68650b7df54f0293fd061972a0fb05aaf4fc0879d3b3d21a638a182c5c543b9f" dependencies = [ "compression-codecs", "compression-core", @@ -412,9 +270,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.37.0" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c34dda4df7017c8db52132f0f8a2e0f8161649d15723ed63fc00c82d0f2081a" +checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549" dependencies = [ "cc", "cmake", @@ -458,9 +316,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "bitvec" @@ -590,9 +448,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.55" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ "find-msvc-tools", "jobserver", @@ -836,11 +694,13 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpio-archive" version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f11d34b07689c21889fc89bd7cc885b3244b0157bbededf4a1c159832cd0df05" dependencies = [ "chrono", "is_executable", "simple-file-manifest", - "thiserror 2.0.18", + "thiserror 1.0.69", ] [[package]] @@ -915,9 +775,9 @@ dependencies = [ [[package]] name = "crypto-bigint" -version = "0.7.0-rc.25" +version = "0.7.0-rc.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cba9eeeb213f7fd29353032f71f7c173e5f6d95d85151cb3a47197b0ea7e8be7" +checksum = "b43308b9b6a47554f4612d5b1fb95ff935040aa3927dd42b1d6cbc015a262d96" dependencies = [ "cpubits", "ctutils", @@ -951,32 +811,15 @@ dependencies = [ [[package]] name = "crypto-primes" -version = "0.7.0-pre.8" +version = "0.7.0-pre.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "334a79c97c0b7fa536716dc132fd417d0afbf471440a41fc25a5d9f66d8771cd" +checksum = "6081ce8b60c0e533e2bba42771b94eb6149052115f4179744d5779883dc98583" dependencies = [ - "crypto-bigint 0.7.0-rc.25", + "crypto-bigint 0.7.0-rc.27", "libm", "rand_core 0.10.0", ] -[[package]] -name = "cryptographic-message-syntax" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c67889c5743b5987961460fbc999dbfa1bb4c7a8e5bfe37eb4d5e678ccb6d1" -dependencies = [ - "bcder", - "bytes", - "chrono", - "hex", - "pem", - "reqwest 0.12.28", - "ring", - "signature 2.2.0", - "x509-certificate", -] - [[package]] name = "ctr" version = "0.10.0-rc.3" @@ -995,32 +838,6 @@ dependencies = [ "cmov", ] -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "curve25519-dalek-derive", - "fiat-crypto", - "rand_core 0.6.4", - "rustc_version", - "subtle", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "data-encoding" version = "2.10.0" @@ -1063,9 +880,9 @@ dependencies = [ [[package]] name = "der" -version = "0.8.0-rc.10" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c1d73e9668ea6b6a28172aa55f3ebec38507131ce179051c8033b5c6037653" +checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" dependencies = [ "const-oid 0.10.2", "pem-rfc7468 1.0.0", @@ -1078,7 +895,7 @@ version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" dependencies = [ - "asn1-rs 0.7.1", + "asn1-rs", "displaydoc", "nom", "num-bigint", @@ -1099,9 +916,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "cc3dc5ad92c2e2d1c193bbbbdf2ea477cb81331de4f3103f267ca18368b988c4" dependencies = [ "powerfmt", ] @@ -1148,14 +965,14 @@ dependencies = [ [[package]] name = "digest" -version = "0.11.0-rc.11" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b42f1d9edf5207c137646b568a0168ca0ec25b7f9eaf7f9961da51a3d91cea" +checksum = "f8bf3682cdec91817be507e4aa104314898b95b84d74f3d43882210101a545b6" dependencies = [ "block-buffer 0.11.0", "const-oid 0.10.2", "crypto-common 0.2.0", - "subtle", + "ctutils", ] [[package]] @@ -1304,12 +1121,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - [[package]] name = "figment" version = "0.10.19" @@ -1547,9 +1358,9 @@ dependencies = [ [[package]] name = "ghash" -version = "0.6.0-rc.5" +version = "0.6.0-rc.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f484be0236661c5ba22d445ed75d3624ba5544541c647549f867fb576e55b2a2" +checksum = "bb9be1ab8718f9d16384cb3626a5a7d7eac4d3fd1b2564e97592f40523dc0228" dependencies = [ "polyval", ] @@ -1637,15 +1448,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac 0.12.1", -] - [[package]] name = "hmac" version = "0.12.1" @@ -1661,7 +1463,7 @@ version = "0.13.0-rc.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef451d73f36d8a3f93ad32c332ea01146c9650e1ec821a9b0e46c01277d544f8" dependencies = [ - "digest 0.11.0-rc.11", + "digest 0.11.0", ] [[package]] @@ -1744,12 +1546,10 @@ dependencies = [ "hyper", "hyper-util", "rustls", - "rustls-native-certs", "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", - "webpki-roots 1.0.6", ] [[package]] @@ -1890,9 +1690,9 @@ checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" [[package]] name = "idevice" -version = "0.1.52" +version = "0.1.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4031af51250d2f22f61a0d7fb7ea71ba8b6144b2b9dd3b7ee4a931fccbd1ec0" +checksum = "18fa17477a6eee7e2e0252302b303a7156c52355b4af7915877b56a3b426fa91" dependencies = [ "async_zip", "base64", @@ -2004,7 +1804,6 @@ version = "0.2.0" dependencies = [ "aes 0.9.0-rc.4", "aes-gcm", - "apple-codesign", "async-trait", "base64", "cbc 0.2.0-rc.3", @@ -2012,6 +1811,8 @@ dependencies = [ "hex", "hmac 0.13.0-rc.5", "idevice", + "isideload-apple-codesign", + "isideload-x509-certificate", "keyring", "p12-keystore", "pbkdf2 0.13.0-rc.9", @@ -2019,7 +1820,7 @@ dependencies = [ "plist-macro", "rand 0.10.0", "rcgen", - "reqwest 0.13.2", + "reqwest", "rootcause", "rsa 0.10.0-rc.15", "serde", @@ -2031,10 +1832,166 @@ dependencies = [ "tokio-tungstenite", "tracing", "uuid", - "x509-certificate", "zip", ] +[[package]] +name = "isideload-apple-codesign" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efff08330c9cd8321487bcc92195c53b75297df56082e2b26c657883ce43e6dd" +dependencies = [ + "anyhow", + "apple-bundles", + "aws-lc-rs", + "base64", + "bcder", + "bitflags", + "bytes", + "chrono", + "clap", + "der 0.7.10", + "dialoguer", + "difference", + "digest 0.10.7", + "dirs", + "elliptic-curve", + "env_logger", + "figment", + "filetime", + "glob", + "goblin", + "hex", + "isideload-apple-flat-package", + "isideload-apple-xar", + "isideload-cryptographic-message-syntax", + "isideload-x509-certificate", + "log", + "md-5", + "num-traits", + "object", + "oid-registry", + "once_cell", + "p12", + "p256", + "pem", + "pkcs1 0.7.5", + "pkcs8 0.10.2", + "plist", + "rand 0.8.5", + "rasn", + "rayon", + "regex", + "reqwest", + "rsa 0.9.10", + "scroll", + "security-framework 2.11.1", + "security-framework-sys", + "semver", + "serde", + "serde_json", + "serde_yaml", + "sha2 0.10.9", + "signature 2.2.0", + "simple-file-manifest", + "spki 0.7.3", + "subtle", + "tempfile", + "thiserror 2.0.18", + "tokio", + "uuid", + "walkdir", + "widestring", + "windows-sys 0.59.0", + "x509", + "xml-rs", + "yasna", + "zeroize", + "zip", + "zip_structs", +] + +[[package]] +name = "isideload-apple-flat-package" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf597e564555d5a16149f5e6250254f256017cf6a60f105ecf64500f4adb599" +dependencies = [ + "cpio-archive", + "flate2", + "isideload-apple-xar", + "scroll", + "serde", + "serde-xml-rs", + "thiserror 2.0.18", +] + +[[package]] +name = "isideload-apple-xar" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6a738c2bd985a6c7d4572d4082806c9928f154c61ac356718d2379186ee30a9" +dependencies = [ + "base64", + "bcder", + "bzip2", + "chrono", + "digest 0.10.7", + "flate2", + "isideload-cryptographic-message-syntax", + "isideload-x509-certificate", + "log", + "md-5", + "rand 0.8.5", + "reqwest", + "scroll", + "serde", + "serde-xml-rs", + "sha1", + "sha2 0.10.9", + "signature 2.2.0", + "thiserror 2.0.18", + "url", + "xml-rs", + "xz2", +] + +[[package]] +name = "isideload-cryptographic-message-syntax" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eef54b517a50bcd4abdb198c600976abc97cde30d71c37e1a0aa0652089ab128" +dependencies = [ + "aws-lc-rs", + "bcder", + "bytes", + "chrono", + "hex", + "isideload-x509-certificate", + "pem", + "reqwest", + "signature 2.2.0", +] + +[[package]] +name = "isideload-x509-certificate" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8f6640892423dd86bac32808e94d586595b3e09a8e3b99ca101dc570d084bc" +dependencies = [ + "aws-lc-rs", + "bcder", + "bytes", + "chrono", + "der 0.7.10", + "hex", + "pem", + "signature 2.2.0", + "spki 0.7.3", + "thiserror 2.0.18", + "zeroize", +] + [[package]] name = "itertools" version = "0.13.0" @@ -2127,7 +2084,7 @@ dependencies = [ "linux-keyutils", "log", "security-framework 2.11.1", - "security-framework 3.5.1", + "security-framework 3.6.0", "windows-sys 0.60.2", "zeroize", ] @@ -2169,9 +2126,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.180" +version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "libdbus-sys" @@ -2256,9 +2213,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "mime" @@ -2266,26 +2223,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "minicbor" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0452a60c1863c1f50b5f77cd295e8d2786849f35883f0b9e18e7e6e1b5691b0" -dependencies = [ - "minicbor-derive", -] - -[[package]] -name = "minicbor-derive" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd2209fff77f705b00c737016a48e73733d7fbccb8b007194db148f03561fb70" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "minimal" version = "0.1.0" @@ -2421,22 +2358,13 @@ dependencies = [ "ruzstd", ] -[[package]] -name = "oid-registry" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" -dependencies = [ - "asn1-rs 0.6.2", -] - [[package]] name = "oid-registry" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" dependencies = [ - "asn1-rs 0.7.1", + "asn1-rs", ] [[package]] @@ -2536,7 +2464,7 @@ version = "0.13.0-rc.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8dfa4e14084d963d35bfb4cdb38712cde78dcf83054c0e8b9b8e899150f374e" dependencies = [ - "digest 0.11.0-rc.11", + "digest 0.11.0", "hmac 0.13.0-rc.5", ] @@ -2646,7 +2574,7 @@ version = "0.8.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "986d2e952779af96ea048f160fd9194e1751b4faea78bcf3ceb456efe008088e" dependencies = [ - "der 0.8.0-rc.10", + "der 0.8.0", "spki 0.8.0-rc.4", ] @@ -2692,11 +2620,11 @@ dependencies = [ [[package]] name = "pkcs8" -version = "0.11.0-rc.10" +version = "0.11.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b226d2cc389763951db8869584fd800cbbe2962bf454e2edeb5172b31ee99774" +checksum = "12922b6296c06eb741b02d7b5161e3aaa22864af38dfa025a1a3ba3f68c84577" dependencies = [ - "der 0.8.0-rc.10", + "der 0.8.0", "spki 0.8.0-rc.4", ] @@ -2736,12 +2664,12 @@ dependencies = [ [[package]] name = "polyval" -version = "0.7.0-rc.7" +version = "0.7.0-rc.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63641a86fddf4b5274f31c43734458ec7acd3133016dbaa37e4e247e1e9acd46" +checksum = "0e83fbff7a079b2d37c70aa6bd5eedb9e5d09ceb9b4ecd31e9ea212d00e9b0bc" dependencies = [ "cpubits", - "cpufeatures 0.2.17", + "cpufeatures 0.3.0", "universal-hash", ] @@ -3118,48 +3046,6 @@ version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" -[[package]] -name = "reqwest" -version = "0.12.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" -dependencies = [ - "base64", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-native-certs", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots 1.0.6", -] - [[package]] name = "reqwest" version = "0.13.2" @@ -3169,7 +3055,9 @@ dependencies = [ "base64", "bytes", "encoding_rs", + "futures-channel", "futures-core", + "futures-util", "h2", "http", "http-body", @@ -3263,11 +3151,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b342b99544549f37509ed7fd42b0cea04bfd9ce07c16ca56094cf0fbeefbbcd" dependencies = [ "const-oid 0.10.2", - "crypto-bigint 0.7.0-rc.25", + "crypto-bigint 0.7.0-rc.27", "crypto-primes", - "digest 0.11.0-rc.11", + "digest 0.11.0", "pkcs1 0.8.0-rc.4", - "pkcs8 0.11.0-rc.10", + "pkcs8 0.11.0-rc.11", "rand_core 0.10.0", "signature 3.0.0-rc.10", "spki 0.8.0-rc.4", @@ -3280,15 +3168,6 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - [[package]] name = "rusticata-macros" version = "4.1.0" @@ -3319,7 +3198,6 @@ checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" dependencies = [ "aws-lc-rs", "once_cell", - "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -3335,7 +3213,7 @@ dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.5.1", + "security-framework 3.6.0", ] [[package]] @@ -3362,7 +3240,7 @@ dependencies = [ "rustls-native-certs", "rustls-platform-verifier-android", "rustls-webpki", - "security-framework 3.5.1", + "security-framework 3.6.0", "security-framework-sys", "webpki-root-certs", "windows-sys 0.61.2", @@ -3493,9 +3371,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.5.1" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +checksum = "d17b898a6d6948c3a8ee4372c17cb384f90d2e6e912ef00895b14fd7ab54ec38" dependencies = [ "bitflags", "core-foundation 0.10.1", @@ -3506,9 +3384,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "321c8673b092a9a42605034a9879d73cb79101ed5fd117bc9a597b89b4e9e61a" dependencies = [ "core-foundation-sys", "libc", @@ -3584,18 +3462,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - [[package]] name = "serde_yaml" version = "0.9.34+deprecated" @@ -3649,7 +3515,7 @@ checksum = "7c5f3b1e2dc8aad28310d8410bd4d7e180eca65fca176c52ab00d364475d0024" dependencies = [ "cfg-if", "cpufeatures 0.2.17", - "digest 0.11.0-rc.11", + "digest 0.11.0", ] [[package]] @@ -3689,7 +3555,7 @@ version = "3.0.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f1880df446116126965eeec169136b2e0251dba37c6223bcc819569550edea3" dependencies = [ - "digest 0.11.0-rc.11", + "digest 0.11.0", "rand_core 0.10.0", ] @@ -3748,18 +3614,6 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "spake2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5482afe85a0b6ce956c945401598dbc527593c77ba51d0a87a586938b1b893a" -dependencies = [ - "curve25519-dalek", - "hkdf", - "rand_core 0.6.4", - "sha2 0.10.9", -] - [[package]] name = "spin" version = "0.9.8" @@ -3783,7 +3637,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8baeff88f34ed0691978ec34440140e1572b68c7dd4a495fd14a3dc1944daa80" dependencies = [ "base64ct", - "der 0.8.0-rc.10", + "der 0.8.0", ] [[package]] @@ -3792,9 +3646,9 @@ version = "0.7.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0b7d0b0d27b2475e779315c09af698ac9f6d40016bc011bf3fa0f0054ce38ed" dependencies = [ - "crypto-bigint 0.7.0-rc.25", + "crypto-bigint 0.7.0-rc.27", "crypto-common 0.2.0", - "digest 0.11.0-rc.11", + "digest 0.11.0", "subtle", ] @@ -3824,9 +3678,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.114" +version = "2.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "6e614ed320ac28113fa64972c4262d5dbc89deacdfd00c34a3e4cea073243c12" dependencies = [ "proc-macro2", "quote", @@ -4255,9 +4109,9 @@ dependencies = [ [[package]] name = "typed-path" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3015e6ce46d5ad8751e4a772543a30c7511468070e98e64e20165f8f81155b64" +checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" [[package]] name = "typenum" @@ -4291,9 +4145,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e" [[package]] name = "unicode-width" @@ -4367,11 +4221,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.20.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" +checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.1", "js-sys", "wasm-bindgen", ] @@ -5086,37 +4940,18 @@ dependencies = [ "spki 0.7.3", ] -[[package]] -name = "x509-certificate" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca9eb9a0c822c67129d5b8fcc2806c6bc4f50496b420825069a440669bcfbf7f" -dependencies = [ - "bcder", - "bytes", - "chrono", - "der 0.7.10", - "hex", - "pem", - "ring", - "signature 2.2.0", - "spki 0.7.3", - "thiserror 2.0.18", - "zeroize", -] - [[package]] name = "x509-parser" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460" dependencies = [ - "asn1-rs 0.7.1", + "asn1-rs", "data-encoding", "der-parser", "lazy_static", "nom", - "oid-registry 0.8.1", + "oid-registry", "rusticata-macros", "thiserror 2.0.18", "time", @@ -5128,13 +4963,13 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202" dependencies = [ - "asn1-rs 0.7.1", + "asn1-rs", "aws-lc-rs", "data-encoding", "der-parser", "lazy_static", "nom", - "oid-registry 0.8.1", + "oid-registry", "rusticata-macros", "thiserror 2.0.18", "time", @@ -5195,18 +5030,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.38" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57cf3aa6855b23711ee9852dfc97dfaa51c45feaba5b645d0c777414d494a961" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.38" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a616990af1a287837c4fe6596ad77ef57948f787e46ce28e166facc0cc1cb75" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" dependencies = [ "proc-macro2", "quote", @@ -5320,9 +5155,9 @@ checksum = "a7948af682ccbc3342b6e9420e8c51c1fe5d7bf7756002b4a3c6cabfe96a7e3c" [[package]] name = "zmij" -version = "1.0.19" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] name = "zopfli" diff --git a/README.md b/README.md index 520cad3..7539f84 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,17 @@ Things left todo before the rewrite is considered finished - Proper entitlement handling - actually parse macho files and stuff, right now it just uses the bare minimum and applies extra entitlements for livecontainer -- Remove dependency on ring and reduce duplicate dependencies +- Reduce duplicate dependencies - partially just need to wait for the rust crypto ecosystem to get through another release cycle -- More parallelism/caching for better performance +- More parallelism and caching for better performance ## Licensing This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. + +## Credits + +- The [idevice](https://github.com/jkcoxson/idevice) crate is used to communicate with the device +- A [modified version of apple-platform-rs](https://github.com/nab138/isideload-apple-platform-rs) was used for codesigning +- [Sideloader](https://github.com/Dadoum/Sideloader) was used as a reference for how some of the private API endpoints work +- [Impactor](https://github.com/khcrysalis/Impactor) was used as a reference for some cryptography code diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index a38ff98..6d740de 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -19,7 +19,7 @@ use tracing_subscriber::FmtSubscriber; async fn main() { isideload::init().expect("Failed to initialize error reporting"); let subscriber = FmtSubscriber::builder() - .with_max_level(Level::DEBUG) + .with_max_level(Level::INFO) .finish(); tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 28433ac..7a7ba50 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -45,9 +45,8 @@ aes-gcm = "0.11.0-rc.3" rsa = { version = "0.10.0-rc.15" } tokio = { version = "1.49.0", features = ["fs"] } keyring = { version = "3.6.3", features = ["apple-native", "linux-native-sync-persistent", "windows-native"], optional = true } -# TODO: Fork to update dependencies (doubt it will ever be updated) -x509-certificate = "0.25" +x509-certificate = { version = "0.25.0", package = "isideload-x509-certificate" } rcgen = { version = "0.14.7", default-features = false, features = ["aws_lc_rs", "pem"] } p12-keystore = "0.2.0" zip = { version = "7.4", default-features = false, features = ["deflate"] } -apple-codesign = { path = "../../plume-apple-platform-rs/apple-codesign", default-features = false} +apple-codesign = { version = "0.29.0", package = "isideload-apple-codesign", default-features = false} From ccc8a685bf50241e6b0f1e3896c74a22ae43e8e7 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 14 Feb 2026 13:11:47 -0500 Subject: [PATCH 61/71] Fix linux build and add increased memory entitlement support --- .github/workflows/build.yml | 6 +++++ README.md | 2 +- examples/minimal/src/main.rs | 2 +- isideload/src/auth/grandslam.rs | 9 +++++++ isideload/src/dev/app_ids.rs | 36 ++++++++++++++++++++++++++ isideload/src/dev/developer_session.rs | 30 +++++++++++++++------ isideload/src/sideload/sideloader.rs | 13 ++++++++-- 7 files changed, 86 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 59a7122..029f62b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,6 +43,12 @@ jobs: - name: Install Rust stable uses: dtolnay/rust-toolchain@stable + - name: Install Linux dependencies + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y pkg-config libdbus-1-dev + - name: Build run: cargo build -p minimal diff --git a/README.md b/README.md index 7539f84..c07ad76 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,6 @@ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file ## Credits - The [idevice](https://github.com/jkcoxson/idevice) crate is used to communicate with the device -- A [modified version of apple-platform-rs](https://github.com/nab138/isideload-apple-platform-rs) was used for codesigning +- A [modified version of apple-platform-rs](https://github.com/nab138/isideload-apple-platform-rs) was used for codesigning, based off [plume-apple-platform-rs](https://github.com/plumeimpactor/plume-apple-platform-rs) - [Sideloader](https://github.com/Dadoum/Sideloader) was used as a reference for how some of the private API endpoints work - [Impactor](https://github.com/khcrysalis/Impactor) was used as a reference for some cryptography code diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 6d740de..43fa902 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -132,7 +132,7 @@ async fn main() { .machine_name("isideload-minimal".to_string()) .build(); - let result = sideloader.install_app(&provider, app_path).await; + let result = sideloader.install_app(&provider, app_path, true).await; match result { Ok(_) => println!("App installed successfully"), Err(e) => panic!("Failed to install app: {:?}", e), diff --git a/isideload/src/auth/grandslam.rs b/isideload/src/auth/grandslam.rs index 006d836..3bd88fe 100644 --- a/isideload/src/auth/grandslam.rs +++ b/isideload/src/auth/grandslam.rs @@ -97,6 +97,15 @@ impl GrandSlam { Ok(builder) } + pub fn patch(&self, url: &str) -> Result { + let builder = self + .client + .patch(url) + .headers(Self::base_headers(&self.client_info, false)?); + + Ok(builder) + } + pub async fn plist_request( &self, url: &str, diff --git a/isideload/src/dev/app_ids.rs b/isideload/src/dev/app_ids.rs index 58e6454..2187b1e 100644 --- a/isideload/src/dev/app_ids.rs +++ b/isideload/src/dev/app_ids.rs @@ -8,6 +8,7 @@ use crate::{ }; use plist::{Data, Date, Dictionary, Value}; use plist_macro::plist; +use reqwest::header::HeaderValue; use rootcause::prelude::*; use serde::Deserialize; @@ -171,6 +172,41 @@ pub trait AppIdsApi { Ok(response) } + + async fn add_increased_memory_limit( + &mut self, + team: &DeveloperTeam, + app_id: &AppId, + ) -> Result<(), Report> { + let dev_session = self.developer_session(); + + let mut headers = dev_session.get_headers().await?; + headers.insert( + "Content-Type", + HeaderValue::from_static("application/vnd.api+json"), + ); + headers.insert( + "Accept", + HeaderValue::from_static("application/vnd.api+json"), + ); + + dev_session + .get_grandslam_client() + .patch(&format!( + "https://developerservices2.apple.com/services/v1/bundleIds/{}", + app_id.app_id_id + ))? + .headers(headers) + .body(format!( + "{{\"data\":{{\"relationships\":{{\"bundleIdCapabilities\":{{\"data\":[{{\"relationships\":{{\"capability\":{{\"data\":{{\"id\":\"INCREASED_MEMORY_LIMIT\",\"type\":\"capabilities\"}}}}}},\"type\":\"bundleIdCapabilities\",\"attributes\":{{\"settings\":[],\"enabled\":true}}}}]}}}},\"id\":\"{}\",\"attributes\":{{\"hasExclusiveManagedCapabilities\":false,\"teamId\":\"{}\",\"bundleType\":\"bundle\",\"identifier\":\"{}\",\"seedId\":\"{}\",\"name\":\"{}\"}},\"type\":\"bundleIds\"}}}}", + app_id.app_id_id, team.team_id, app_id.identifier, team.team_id, app_id.name + )) + .send() + .await.context("Failed to request increased memory entitlement")? + .error_for_status().context("Failed to add increased memory entitlement")?; + + Ok(()) + } } impl AppIdsApi for DeveloperSession { diff --git a/isideload/src/dev/developer_session.rs b/isideload/src/dev/developer_session.rs index a5ee76d..f8f61d0 100644 --- a/isideload/src/dev/developer_session.rs +++ b/isideload/src/dev/developer_session.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use plist::Dictionary; use plist_macro::{plist, plist_to_xml_string}; +use reqwest::header::{HeaderMap, HeaderValue}; use rootcause::prelude::*; use serde::de::DeserializeOwned; use tracing::{error, warn}; @@ -65,6 +66,26 @@ impl DeveloperSession { )) } + pub async fn get_headers(&mut self) -> Result { + let mut headers = self + .anisette_generator + .get_anisette_data(self.client.clone()) + .await? + .get_header_map(); + + headers.insert( + "X-Apple-GS-Token", + HeaderValue::from_str(&self.token.token)?, + ); + headers.insert("X-Apple-I-Identity-Id", HeaderValue::from_str(&self.adsid)?); + + Ok(headers) + } + + pub fn get_grandslam_client(&self) -> Arc { + self.client.clone() + } + async fn send_dev_request_internal( &mut self, url: &str, @@ -85,14 +106,7 @@ impl DeveloperSession { .client .post(url)? .body(plist_to_xml_string(&body)) - .header("X-Apple-GS-Token", &self.token.token) - .header("X-Apple-I-Identity-Id", &self.adsid) - .headers( - self.anisette_generator - .get_anisette_data(self.client.clone()) - .await? - .get_header_map(), - ) + .headers(self.get_headers().await?) .send() .await? .error_for_status() diff --git a/isideload/src/sideload/sideloader.rs b/isideload/src/sideload/sideloader.rs index 377ecf8..7efcf14 100644 --- a/isideload/src/sideload/sideloader.rs +++ b/isideload/src/sideload/sideloader.rs @@ -63,6 +63,8 @@ impl Sideloader { &mut self, app_path: PathBuf, team: Option, + // this will be replaced with proper entitlement handling later + increased_memory_limit: bool, ) -> Result { let team = match team { Some(t) => t, @@ -128,7 +130,11 @@ impl Sideloader { .assign_app_group(&team, &app_group, app_id, None) .await?; - // TODO: Increased memory entitlement + if increased_memory_limit { + self.dev_session + .add_increased_memory_limit(&team, app_id) + .await?; + } } app.apply_special_app_behavior(&special, &group_identifier, &cert_identity) @@ -189,6 +195,7 @@ impl Sideloader { &mut self, device_provider: &impl IdeviceProvider, app_path: PathBuf, + increased_memory_limit: bool, ) -> Result<(), Report> { let device_info = IdeviceInfo::from_device(device_provider).await?; @@ -197,7 +204,9 @@ impl Sideloader { .ensure_device_registered(&team, &device_info.name, &device_info.udid, None) .await?; - let signed_app_path = self.sign_app(app_path, Some(team)).await?; + let signed_app_path = self + .sign_app(app_path, Some(team), increased_memory_limit) + .await?; info!("Installing..."); From adc81774b04cb41bfe3f8b09aa99648d02950ee0 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 14 Feb 2026 15:07:31 -0500 Subject: [PATCH 62/71] Windows fix --- isideload/src/sideload/sideloader.rs | 3 ++- isideload/src/util/keyring_storage.rs | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/isideload/src/sideload/sideloader.rs b/isideload/src/sideload/sideloader.rs index 7efcf14..5501e38 100644 --- a/isideload/src/sideload/sideloader.rs +++ b/isideload/src/sideload/sideloader.rs @@ -78,7 +78,8 @@ impl Sideloader { self.storage.as_ref(), &self.max_certs_behavior, ) - .await?; + .await + .context("Failed to retrieve certificate identity")?; let mut app = Application::new(app_path)?; let special = app.get_special_app(); diff --git a/isideload/src/util/keyring_storage.rs b/isideload/src/util/keyring_storage.rs index 0ebaa34..2b7a78a 100644 --- a/isideload/src/util/keyring_storage.rs +++ b/isideload/src/util/keyring_storage.rs @@ -43,4 +43,22 @@ impl SideloadingStorage for KeyringStorage { Err(e) => Err(e.into()), } } + + // Linux doesn't seem to properly retrive binary secrets, so we don't use this implementation and instead let it fall back to base64 encoding. + // Windows fails to store the base64 encoded data because it is too long. + #[cfg(target_os = "windows")] + fn store_data(&self, key: &str, value: &[u8]) -> Result<(), Report> { + Entry::new(&self.service_name, key)?.set_secret(value)?; + Ok(()) + } + + #[cfg(target_os = "windows")] + fn retrieve_data(&self, key: &str) -> Result>, Report> { + let entry = Entry::new(&self.service_name, key)?; + match entry.get_secret() { + Ok(secret) => Ok(Some(secret)), + Err(keyring::Error::NoEntry) => Ok(None), + Err(e) => Err(e.into()), + } + } } From a62ce2863dd513a4b801a0ffb3588b6ff0de3715 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 14 Feb 2026 15:19:59 -0500 Subject: [PATCH 63/71] Improve logging and small refactor --- isideload/src/sideload/application.rs | 1 + isideload/src/sideload/mod.rs | 1 + isideload/src/sideload/sideloader.rs | 112 +++++-------------------- isideload/src/sideload/sign.rs | 114 ++++++++++++++++++++++++++ 4 files changed, 135 insertions(+), 93 deletions(-) create mode 100644 isideload/src/sideload/sign.rs diff --git a/isideload/src/sideload/application.rs b/isideload/src/sideload/application.rs index 8cf531c..4a33ecb 100644 --- a/isideload/src/sideload/application.rs +++ b/isideload/src/sideload/application.rs @@ -208,6 +208,7 @@ impl Application { }) .collect(); + info!("Registered app IDs"); Ok(app_ids) } diff --git a/isideload/src/sideload/mod.rs b/isideload/src/sideload/mod.rs index d0258ed..5e59ac5 100644 --- a/isideload/src/sideload/mod.rs +++ b/isideload/src/sideload/mod.rs @@ -5,4 +5,5 @@ pub mod cert_identity; #[cfg(feature = "install")] pub mod install; pub mod sideloader; +pub mod sign; pub use builder::{SideloaderBuilder, TeamSelection}; diff --git a/isideload/src/sideload/sideloader.rs b/isideload/src/sideload/sideloader.rs index 5501e38..a1a0272 100644 --- a/isideload/src/sideload/sideloader.rs +++ b/isideload/src/sideload/sideloader.rs @@ -11,17 +11,15 @@ use crate::{ application::{Application, SpecialApp}, builder::MaxCertsBehavior, cert_identity::CertificateIdentity, + sign, }, - util::{device::IdeviceInfo, plist::PlistDataExtract, storage::SideloadingStorage}, + util::{device::IdeviceInfo, storage::SideloadingStorage}, }; use std::path::PathBuf; -use apple_codesign::{SigningSettings, UnifiedSigner}; use idevice::provider::IdeviceProvider; -use plist::Dictionary; -use plist_macro::plist_to_xml_string; -use rootcause::{option_ext::OptionExt, prelude::*}; +use rootcause::prelude::*; use tracing::info; pub struct Sideloader { @@ -65,7 +63,7 @@ impl Sideloader { team: Option, // this will be replaced with proper entitlement handling later increased_memory_limit: bool, - ) -> Result { + ) -> Result<(PathBuf, Option), Report> { let team = match team { Some(t) => t, None => self.get_team().await?, @@ -138,6 +136,8 @@ impl Sideloader { } } + info!("App IDs configured"); + app.apply_special_app_behavior(&special, &group_identifier, &cert_identity) .await .context("Failed to modify app bundle")?; @@ -147,6 +147,8 @@ impl Sideloader { .download_team_provisioning_profile(&team, &main_app_id, None) .await?; + info!("Acquired provisioning profile"); + app.bundle.write_info()?; for ext in app.bundle.app_extensions_mut() { ext.write_info()?; @@ -161,34 +163,18 @@ impl Sideloader { ) .await?; - let mut settings = Self::signing_settings(&cert_identity)?; - let entitlements: Dictionary = Self::entitlements_from_prov( - provisioning_profile.encoded_profile.as_ref(), + sign::sign( + &mut app, + &cert_identity, + &provisioning_profile, &special, &team, - )?; - - settings - .set_entitlements_xml( - apple_codesign::SettingsScope::Main, - plist_to_xml_string(&entitlements), - ) - .context("Failed to set entitlements XML")?; - let signer = UnifiedSigner::new(settings); - - for bundle in app.bundle.collect_bundles_sorted() { - info!("Signing bundle {}", bundle.bundle_dir.display()); - signer - .sign_path_in_place(&bundle.bundle_dir) - .context(format!( - "Failed to sign bundle: {}", - bundle.bundle_dir.display() - ))?; - } + ) + .context("Failed to sign app")?; info!("App signed!"); - Ok(app.bundle.bundle_dir.clone()) + Ok((app.bundle.bundle_dir.clone(), special)) } #[cfg(feature = "install")] @@ -197,7 +183,7 @@ impl Sideloader { device_provider: &impl IdeviceProvider, app_path: PathBuf, increased_memory_limit: bool, - ) -> Result<(), Report> { + ) -> Result, Report> { let device_info = IdeviceInfo::from_device(device_provider).await?; let team = self.get_team().await?; @@ -205,11 +191,11 @@ impl Sideloader { .ensure_device_registered(&team, &device_info.name, &device_info.udid, None) .await?; - let signed_app_path = self + let (signed_app_path, special_app) = self .sign_app(app_path, Some(team), increased_memory_limit) .await?; - info!("Installing..."); + info!("Transferring App..."); crate::sideload::install::install_app(device_provider, &signed_app_path, |progress| { info!("Installing: {}%", progress); @@ -217,7 +203,7 @@ impl Sideloader { .await .context("Failed to install app on device")?; - Ok(()) + Ok(special_app) } /// Get the developer team according to the configured team selection behavior pub async fn get_team(&mut self) -> Result { @@ -246,64 +232,4 @@ impl Sideloader { } }) } - - pub fn signing_settings<'a>( - cert: &'a CertificateIdentity, - ) -> Result, Report> { - let mut settings = SigningSettings::default(); - - cert.setup_signing_settings(&mut settings)?; - settings.set_for_notarization(false); - settings.set_shallow(true); - - Ok(settings) - } - - fn entitlements_from_prov( - data: &[u8], - special: &Option, - team: &DeveloperTeam, - ) -> Result { - let start = data - .windows(6) - .position(|w| w == b"") - .ok_or_report()? - + 8; - let plist_data = &data[start..end]; - let plist = plist::Value::from_reader_xml(plist_data)?; - - let mut entitlements = plist - .as_dictionary() - .ok_or_report()? - .get_dict("Entitlements")? - .clone(); - - if matches!( - special, - Some(SpecialApp::SideStoreLc) | Some(SpecialApp::LiveContainer) - ) { - let mut keychain_access = vec![plist::Value::String(format!( - "{}.com.kdt.livecontainer.shared", - team.team_id - ))]; - - for number in 1..128 { - keychain_access.push(plist::Value::String(format!( - "{}.com.kdt.livecontainer.shared.{}", - team.team_id, number - ))); - } - - entitlements.insert( - "keychain-access-groups".to_string(), - plist::Value::Array(keychain_access), - ); - } - - Ok(entitlements) - } } diff --git a/isideload/src/sideload/sign.rs b/isideload/src/sideload/sign.rs new file mode 100644 index 0000000..837116e --- /dev/null +++ b/isideload/src/sideload/sign.rs @@ -0,0 +1,114 @@ +use apple_codesign::{SigningSettings, UnifiedSigner}; +use plist::Dictionary; +use plist_macro::plist_to_xml_string; +use rootcause::{option_ext::OptionExt, prelude::*}; +use tracing::info; + +use crate::{ + dev::{app_ids::Profile, teams::DeveloperTeam}, + sideload::{ + application::{Application, SpecialApp}, + cert_identity::CertificateIdentity, + }, + util::plist::PlistDataExtract, +}; + +pub fn sign( + app: &mut Application, + cert_identity: &CertificateIdentity, + provisioning_profile: &Profile, + special: &Option, + team: &DeveloperTeam, +) -> Result<(), Report> { + let mut settings = signing_settings(cert_identity)?; + let entitlements: Dictionary = entitlements_from_prov( + provisioning_profile.encoded_profile.as_ref(), + &special, + &team, + )?; + + settings + .set_entitlements_xml( + apple_codesign::SettingsScope::Main, + plist_to_xml_string(&entitlements), + ) + .context("Failed to set entitlements XML")?; + let signer = UnifiedSigner::new(settings); + + for bundle in app.bundle.collect_bundles_sorted() { + info!( + "Signing {}", + bundle + .bundle_dir + .file_name() + .unwrap_or(bundle.bundle_dir.as_os_str()) + .to_string_lossy() + ); + signer + .sign_path_in_place(&bundle.bundle_dir) + .context(format!( + "Failed to sign bundle: {}", + bundle.bundle_dir.display() + ))?; + } + + Ok(()) +} + +pub fn signing_settings<'a>(cert: &'a CertificateIdentity) -> Result, Report> { + let mut settings = SigningSettings::default(); + + cert.setup_signing_settings(&mut settings)?; + settings.set_for_notarization(false); + settings.set_shallow(true); + + Ok(settings) +} + +fn entitlements_from_prov( + data: &[u8], + special: &Option, + team: &DeveloperTeam, +) -> Result { + let start = data + .windows(6) + .position(|w| w == b"") + .ok_or_report()? + + 8; + let plist_data = &data[start..end]; + let plist = plist::Value::from_reader_xml(plist_data)?; + + let mut entitlements = plist + .as_dictionary() + .ok_or_report()? + .get_dict("Entitlements")? + .clone(); + + if matches!( + special, + Some(SpecialApp::SideStoreLc) | Some(SpecialApp::LiveContainer) + ) { + let mut keychain_access = vec![plist::Value::String(format!( + "{}.com.kdt.livecontainer.shared", + team.team_id + ))]; + + for number in 1..128 { + keychain_access.push(plist::Value::String(format!( + "{}.com.kdt.livecontainer.shared.{}", + team.team_id, number + ))); + } + + entitlements.insert( + "keychain-access-groups".to_string(), + plist::Value::Array(keychain_access), + ); + } + + Ok(entitlements) +} From 6440d2291507e60a7d4977308798d23d5ec84e48 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 14 Feb 2026 15:21:39 -0500 Subject: [PATCH 64/71] Fix clippy on example --- examples/minimal/src/main.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 43fa902..6abc19a 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -68,9 +68,7 @@ async fn main() { panic!("No devices found"); } - let provider = devs - .iter() - .next() + let provider = devs.first() .unwrap() .to_provider(UsbmuxdAddr::from_env_var().unwrap(), "isideload-demo"); From 7a9cff097dca12deaf9ebf5edda8f30e2af36845 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 14 Feb 2026 15:24:17 -0500 Subject: [PATCH 65/71] Fix clippy --- isideload/src/sideload/application.rs | 10 +++++----- isideload/src/sideload/cert_identity.rs | 12 ++++++------ isideload/src/sideload/sign.rs | 4 ++-- isideload/src/util/storage.rs | 6 ++++++ 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/isideload/src/sideload/application.rs b/isideload/src/sideload/application.rs index 4a33ecb..02a3f8b 100644 --- a/isideload/src/sideload/application.rs +++ b/isideload/src/sideload/application.rs @@ -137,7 +137,7 @@ impl Application { let extensions = self.bundle.app_extensions_mut(); 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()) { + if !(id.starts_with(main_app_bundle_id) && id.len() > main_app_bundle_id.len()) { bail!(SideloadError::InvalidBundle(format!( "Extension {} is not part of the main app bundle identifier: {}", ext.bundle_name().unwrap_or("Unknown"), @@ -152,7 +152,7 @@ impl Application { } } } - self.bundle.set_bundle_identifier(&main_app_id_str); + self.bundle.set_bundle_identifier(main_app_id_str); Ok(()) } @@ -168,7 +168,7 @@ impl Application { bundles_with_app_id.extend(extension_refs); let list_app_ids_response = dev_session - .list_app_ids(&team, None) + .list_app_ids(team, None) .await .context("Failed to list app IDs for the developer team")?; let app_ids_to_register = bundles_with_app_id @@ -195,9 +195,9 @@ impl Application { for bundle in app_ids_to_register { let id = bundle.bundle_identifier().unwrap_or(""); let name = bundle.bundle_name().unwrap_or(""); - dev_session.add_app_id(&team, name, id, None).await?; + dev_session.add_app_id(team, name, id, None).await?; } - let list_app_id_response = dev_session.list_app_ids(&team, None).await?; + let list_app_id_response = dev_session.list_app_ids(team, None).await?; let app_ids: Vec<_> = list_app_id_response .app_ids .into_iter() diff --git a/isideload/src/sideload/cert_identity.rs b/isideload/src/sideload/cert_identity.rs index c8c80be..d98f402 100644 --- a/isideload/src/sideload/cert_identity.rs +++ b/isideload/src/sideload/cert_identity.rs @@ -61,7 +61,7 @@ impl CertificateIdentity { p12_keystore::KeyStoreEntry::PrivateKeyChain(key_chain), ); - let writer = keystore.writer(&password); + let writer = keystore.writer(password); let p12 = writer.write().context("Failed to write PKCS#12 archive")?; Ok(p12) } @@ -127,16 +127,16 @@ impl CertificateIdentity { let email_hash = hex::encode(hasher.finalize()); let private_key = storage.retrieve_data(&format!("{}/key", email_hash))?; - if private_key.is_some() { + if let Some(priv_key) = private_key { info!("Using existing private key from storage"); - return Ok(RsaPrivateKey::from_pkcs8_der(&private_key.unwrap())?); + return Ok(RsaPrivateKey::from_pkcs8_der(&priv_key)?); } let mut rng = rand::rng(); let private_key = RsaPrivateKey::new(&mut rng, 2048)?; storage.store_data( &format!("{}/key", email_hash), - &private_key.to_pkcs8_der()?.as_bytes(), + private_key.to_pkcs8_der()?.as_bytes(), )?; Ok(private_key) @@ -239,7 +239,7 @@ impl CertificateIdentity { *code, "Maximum number of certificates reached".to_string(), ), - &mut existing_certs.as_mut().unwrap(), + existing_certs.as_mut().unwrap(), ) .await?; } else { @@ -297,7 +297,7 @@ impl CertificateIdentity { Ok(()) } else { error!("No more certificates to revoke but still hitting max certs error"); - return Err(error.into()); + Err(error.into()) } } MaxCertsBehavior::Error => Err(error.into()), diff --git a/isideload/src/sideload/sign.rs b/isideload/src/sideload/sign.rs index 837116e..3468405 100644 --- a/isideload/src/sideload/sign.rs +++ b/isideload/src/sideload/sign.rs @@ -23,8 +23,8 @@ pub fn sign( let mut settings = signing_settings(cert_identity)?; let entitlements: Dictionary = entitlements_from_prov( provisioning_profile.encoded_profile.as_ref(), - &special, - &team, + special, + team, )?; settings diff --git a/isideload/src/util/storage.rs b/isideload/src/util/storage.rs index 0480174..50ede4c 100644 --- a/isideload/src/util/storage.rs +++ b/isideload/src/util/storage.rs @@ -39,6 +39,12 @@ pub struct InMemoryStorage { storage: Mutex>, } +impl Default for InMemoryStorage { + fn default() -> Self { + Self::new() + } +} + impl InMemoryStorage { pub fn new() -> Self { InMemoryStorage { From e9c416caa554fcac10a4105466bce119eb4196ad Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 14 Feb 2026 15:28:58 -0500 Subject: [PATCH 66/71] Update example and README --- README.md | 6 ++++++ examples/minimal/README.md | 5 +++++ examples/minimal/src/main.rs | 8 ++------ 3 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 examples/minimal/README.md diff --git a/README.md b/README.md index c07ad76..6fff413 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,12 @@ A Rust library for sideloading iOS applications using an Apple ID. Used in [Cros This branch is home to isideload-next, the next major version of isideload. It features a redesigned API, improved error handling, better entitlement handling, and more. It is not ready! +## Usage + +**You must call `isideload::init()` at the start of your program to ensure that errors are properly reported.** If you don't, errors related to network requests will not show any details. + +A full usage example is available is in [examples/minimal](examples/minimal/). + ## TODO Things left todo before the rewrite is considered finished diff --git a/examples/minimal/README.md b/examples/minimal/README.md new file mode 100644 index 0000000..6a6f7f9 --- /dev/null +++ b/examples/minimal/README.md @@ -0,0 +1,5 @@ +# minimal + +A minimal sideloading CLI to to demonstrate isideload. + +Usage: `minimal ` \ No newline at end of file diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 6abc19a..ec50000 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -46,11 +46,6 @@ async fn main() { .login(apple_password, get_2fa_code) .await; - // match &account { - // Ok(a) => println!("Logged in. {}", a), - // Err(e) => panic!("Failed to log in to Apple ID: {:?}", e), - // } - let mut account = account.unwrap(); let dev_session = DeveloperSession::from_account(&mut account) @@ -68,7 +63,8 @@ async fn main() { panic!("No devices found"); } - let provider = devs.first() + let provider = devs + .first() .unwrap() .to_provider(UsbmuxdAddr::from_env_var().unwrap(), "isideload-demo"); From 1958c10b2d920c1d76ae2d41fe6024df9ee029c4 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 14 Feb 2026 16:25:18 -0500 Subject: [PATCH 67/71] Enable native-tls on windows to avoid needing a rustls-platform-verifier patch --- Cargo.lock | 121 +++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 5 +- isideload/Cargo.toml | 6 +++ 3 files changed, 124 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4aee0f2..8025d2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1181,6 +1181,21 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1552,6 +1567,22 @@ dependencies = [ "tower-service", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.20" @@ -2263,6 +2294,23 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe 0.1.6", + "openssl-sys", + "schannel", + "security-framework 2.11.1", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nom" version = "7.1.3" @@ -2379,12 +2427,56 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "openssl" +version = "0.10.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + [[package]] name = "openssl-probe" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -3064,10 +3156,12 @@ dependencies = [ "http-body-util", "hyper", "hyper-rustls", + "hyper-tls", "hyper-util", "js-sys", "log", "mime", + "native-tls", "percent-encoding", "pin-project-lite", "quinn", @@ -3078,6 +3172,7 @@ dependencies = [ "serde_json", "sync_wrapper", "tokio", + "tokio-native-tls", "tokio-rustls", "tower", "tower-http", @@ -3210,7 +3305,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" dependencies = [ - "openssl-probe", + "openssl-probe 0.2.1", "rustls-pki-types", "schannel", "security-framework 3.6.0", @@ -3229,7 +3324,8 @@ dependencies = [ [[package]] name = "rustls-platform-verifier" version = "0.6.2" -source = "git+https://github.com/cstkingkey/rustls-platform-verifier?branch=extra#1efde30e9080351dc1afd74151ea5b849f1e69f9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" dependencies = [ "core-foundation 0.10.1", "core-foundation-sys", @@ -3243,13 +3339,14 @@ dependencies = [ "security-framework 3.6.0", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] name = "rustls-platform-verifier-android" version = "0.1.1" -source = "git+https://github.com/cstkingkey/rustls-platform-verifier?branch=extra#1efde30e9080351dc1afd74151ea5b849f1e69f9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" @@ -3878,6 +3975,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.4" @@ -4236,6 +4343,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" diff --git a/Cargo.toml b/Cargo.toml index 9dc06e1..3331121 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,4 @@ [workspace] resolver = "2" members = ["examples/minimal","isideload"] -default-members = ["isideload"] - -[patch.crates-io] -rustls-platform-verifier = { git = "https://github.com/cstkingkey/rustls-platform-verifier", branch = "extra" } \ No newline at end of file +default-members = ["isideload"] \ No newline at end of file diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 7a7ba50..b5aa2db 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -50,3 +50,9 @@ rcgen = { version = "0.14.7", default-features = false, features = ["aws_lc_rs", p12-keystore = "0.2.0" zip = { version = "7.4", default-features = false, features = ["deflate"] } apple-codesign = { version = "0.29.0", package = "isideload-apple-codesign", default-features = false} + +# There is a bug in rustls-platform-verifier that causes an invalid certificate error with apple's root cert. +# It has been fixed already but I am waiting for a new release before Ic an update the dependency. +# Using native-tls avoids the issue. +[target.'cfg(windows)'.dependencies] +reqwest = { version = "0.13.2", features = ["json", "gzip", "native-tls"] } \ No newline at end of file From 32cc042c0761229157efc0a567fd4bddb2ebf80b Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 14 Feb 2026 17:23:52 -0500 Subject: [PATCH 68/71] Use storage provider for anisette and provide fs-storage implementation --- examples/minimal/src/main.rs | 4 -- isideload/Cargo.toml | 1 + isideload/src/anisette/remote_v3/mod.rs | 42 ++++++++++++-------- isideload/src/util/fs_storage.rs | 53 +++++++++++++++++++++++++ isideload/src/util/mod.rs | 2 + isideload/src/util/storage.rs | 13 ++++-- 6 files changed, 92 insertions(+), 23 deletions(-) create mode 100644 isideload/src/util/fs_storage.rs diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index ec50000..b6138b2 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -9,7 +9,6 @@ use isideload::{ teams::DeveloperTeam, }, sideload::{SideloaderBuilder, TeamSelection, builder::MaxCertsBehavior}, - util::keyring_storage::KeyringStorage, }; use tracing::Level; @@ -120,9 +119,6 @@ async fn main() { let mut sideloader = SideloaderBuilder::new(dev_session, apple_id.to_string()) .team_selection(TeamSelection::Prompt(team_selection_prompt)) .max_certs_behavior(MaxCertsBehavior::Prompt(cert_selection_prompt)) - .storage(Box::new(KeyringStorage::new( - "isideload-minimal".to_string(), - ))) .machine_name("isideload-minimal".to_string()) .build(); diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index b5aa2db..4f3c5ce 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -14,6 +14,7 @@ readme = "../README.md" default = ["install", "keyring-storage"] install = ["dep:idevice"] keyring-storage = ["keyring"] +fs-storage = [] # Unfortunately, dependencies are kinda a mess rn, since this requires a beta version of the srp crate. # Once that becomes stable, hopefuly duplicate dependencies should clean up.\ diff --git a/isideload/src/anisette/remote_v3/mod.rs b/isideload/src/anisette/remote_v3/mod.rs index 3665a81..e5ddebc 100644 --- a/isideload/src/anisette/remote_v3/mod.rs +++ b/isideload/src/anisette/remote_v3/mod.rs @@ -1,7 +1,5 @@ mod state; -use std::fs; -use std::path::PathBuf; use std::sync::Arc; use std::time::SystemTime; @@ -11,13 +9,14 @@ use reqwest::header::{CONTENT_TYPE, HeaderMap, HeaderValue}; use rootcause::prelude::*; use serde::Deserialize; use tokio_tungstenite::tungstenite::Message; -use tracing::{debug, info}; +use tracing::{debug, info, warn}; use crate::SideloadError; use crate::anisette::remote_v3::state::AnisetteState; use crate::anisette::{AnisetteClientInfo, AnisetteData, AnisetteProvider}; use crate::auth::grandslam::GrandSlam; use crate::util::plist::PlistDataExtract; +use crate::util::storage::{SideloadingStorage, new_storage}; use futures_util::{SinkExt, StreamExt}; pub const DEFAULT_ANISETTE_V3_URL: &str = "https://ani.stikstore.app"; @@ -25,7 +24,7 @@ pub const DEFAULT_ANISETTE_V3_URL: &str = "https://ani.stikstore.app"; pub struct RemoteV3AnisetteProvider { pub state: Option, url: String, - config_path: PathBuf, + storage: Box, serial_number: String, client_info: Option, client: reqwest::Client, @@ -36,14 +35,14 @@ impl RemoteV3AnisetteProvider { /// /// # Arguments /// - `url`: The URL of the remote anisette service - /// - `config_path`: The path to the config file + /// - `storage`: The storage backend for anisette data /// - `serial_number`: The serial number of the device /// - pub fn new(url: &str, config_path: PathBuf, serial_number: String) -> Self { + pub fn new(url: &str, storage: Box, serial_number: String) -> Self { Self { state: None, url: url.to_string(), - config_path, + storage, serial_number, client_info: None, client: reqwest::ClientBuilder::new() @@ -58,8 +57,8 @@ impl RemoteV3AnisetteProvider { self } - pub fn set_config_path(mut self, config_path: PathBuf) -> RemoteV3AnisetteProvider { - self.config_path = config_path; + pub fn set_storage(mut self, storage: Box) -> RemoteV3AnisetteProvider { + self.storage = storage; self } @@ -71,7 +70,11 @@ impl RemoteV3AnisetteProvider { impl Default for RemoteV3AnisetteProvider { fn default() -> Self { - Self::new(DEFAULT_ANISETTE_V3_URL, PathBuf::new(), "0".to_string()) + Self::new( + DEFAULT_ANISETTE_V3_URL, + Box::new(new_storage()), + "0".to_string(), + ) } } @@ -166,12 +169,15 @@ impl AnisetteProvider for RemoteV3AnisetteProvider { impl RemoteV3AnisetteProvider { async fn get_state(&mut self, gs: Arc) -> Result<&mut AnisetteState, Report> { - let state_path = self.config_path.join("state.plist"); - fs::create_dir_all(&self.config_path)?; if self.state.is_none() { - if let Ok(state) = plist::from_file(&state_path) { - info!("Loaded existing anisette state from {:?}", state_path); - self.state = Some(state); + if let Ok(Some(state)) = &self.storage.retrieve_data("anisette_state") { + if let Ok(state) = plist::from_bytes(state) { + info!("Loaded existing anisette state"); + self.state = Some(state); + } else { + warn!("Failed to parse existing anisette state, starting fresh"); + self.state = Some(AnisetteState::new()); + } } else { info!("No existing anisette state found"); self.state = Some(AnisetteState::new()); @@ -185,7 +191,11 @@ impl RemoteV3AnisetteProvider { .await .context("Failed to provision")?; } - plist::to_file_xml(&state_path, &state)?; + let buf = Vec::new(); + let mut writer = std::io::BufWriter::new(buf); + plist::to_writer_xml(&mut writer, &state).unwrap(); + self.storage + .store_data("anisette_state", &writer.into_inner()?)?; Ok(state) } diff --git a/isideload/src/util/fs_storage.rs b/isideload/src/util/fs_storage.rs new file mode 100644 index 0000000..4ccb959 --- /dev/null +++ b/isideload/src/util/fs_storage.rs @@ -0,0 +1,53 @@ +use std::path::{Path, PathBuf}; + +use rootcause::prelude::*; + +use crate::util::storage::SideloadingStorage; + +pub struct FsStorage { + path: PathBuf, +} + +impl FsStorage { + pub fn new(path: PathBuf) -> Self { + FsStorage { path } + } +} + +impl Default for FsStorage { + fn default() -> Self { + Self::new(PathBuf::from(".")) + } +} + +impl SideloadingStorage for FsStorage { + fn store_data(&self, key: &str, data: &[u8]) -> Result<(), Report> { + let path = self.path.join(key); + let parent = path.parent().unwrap_or(Path::new(".")); + std::fs::create_dir_all(parent).context("Failed to create storage directory")?; + std::fs::write(&path, data).context("Failed to write data to file")?; + + Ok(()) + } + + fn retrieve_data(&self, key: &str) -> Result>, Report> { + let path = self.path.join(key); + match std::fs::read(&path) { + Ok(data) => Ok(Some(data)), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None), + Err(e) => Err(report!(e).context("Failed to read data from file").into()), + } + } + + fn store(&self, key: &str, value: &str) -> Result<(), Report> { + self.store_data(key, value.as_bytes()) + } + + fn retrieve(&self, key: &str) -> Result, Report> { + match self.retrieve_data(key) { + Ok(Some(data)) => Ok(Some(String::from_utf8_lossy(&data).into_owned())), + Ok(None) => Ok(None), + Err(e) => Err(e), + } + } +} diff --git a/isideload/src/util/mod.rs b/isideload/src/util/mod.rs index 41d1f65..abc79df 100644 --- a/isideload/src/util/mod.rs +++ b/isideload/src/util/mod.rs @@ -1,4 +1,6 @@ pub mod device; +#[cfg(feature = "fs-storage")] +pub mod fs_storage; #[cfg(feature = "keyring-storage")] pub mod keyring_storage; pub mod plist; diff --git a/isideload/src/util/storage.rs b/isideload/src/util/storage.rs index 50ede4c..9e09a5a 100644 --- a/isideload/src/util/storage.rs +++ b/isideload/src/util/storage.rs @@ -27,11 +27,18 @@ pub trait SideloadingStorage: Send + Sync { pub fn new_storage() -> impl SideloadingStorage { #[cfg(feature = "keyring-storage")] { - crate::util::keyring_storage::KeyringStorage::default() + return crate::util::keyring_storage::KeyringStorage::default(); } - #[cfg(not(feature = "keyring-storage"))] + #[cfg(feature = "fs-storage")] { - InMemoryStorage::new() + return crate::util::fs_storage::FsStorage::default(); + } + #[cfg(not(any(feature = "keyring-storage", feature = "fs-storage")))] + { + tracing::warn!( + "Keyring storage not enabled, falling back to in-memory storage. This means that the anisette state and certificates will not be saved across runs. Enable the 'keyring-storage' or 'fs-storage' feature for persistance." + ); + return InMemoryStorage::new(); } } From eca2f98e72d3f2771efd09542baa8920578a80b9 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 14 Feb 2026 17:24:49 -0500 Subject: [PATCH 69/71] Update README credits --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6fff413..88d897c 100644 --- a/README.md +++ b/README.md @@ -30,5 +30,5 @@ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file - The [idevice](https://github.com/jkcoxson/idevice) crate is used to communicate with the device - A [modified version of apple-platform-rs](https://github.com/nab138/isideload-apple-platform-rs) was used for codesigning, based off [plume-apple-platform-rs](https://github.com/plumeimpactor/plume-apple-platform-rs) -- [Sideloader](https://github.com/Dadoum/Sideloader) was used as a reference for how some of the private API endpoints work -- [Impactor](https://github.com/khcrysalis/Impactor) was used as a reference for some cryptography code +- [Impactor](https://github.com/khcrysalis/Impactor) was used as a reference for cryptography, codesigning, and provision file parsing. +- [Sideloader](https://github.com/Dadoum/Sideloader) was used as a reference for how apple private developer endpoints work From f7926ad9d7b5c65a7b3c9ba151d2c24e390a9335 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 14 Feb 2026 17:37:48 -0500 Subject: [PATCH 70/71] Improve documentation and clean up temporary files --- isideload/src/sideload/builder.rs | 10 ++++++++++ isideload/src/sideload/sideloader.rs | 14 +++++++++++++- isideload/src/util/storage.rs | 2 ++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/isideload/src/sideload/builder.rs b/isideload/src/sideload/builder.rs index 096601e..ba17b87 100644 --- a/isideload/src/sideload/builder.rs +++ b/isideload/src/sideload/builder.rs @@ -79,9 +79,11 @@ pub struct SideloaderBuilder { //extensions_behavior: Option, storage: Option>, machine_name: Option, + delete_app_after_install: bool, } impl SideloaderBuilder { + /// Create a new `SideloaderBuilder` with the provided Apple developer session and Apple ID email. pub fn new(developer_session: DeveloperSession, apple_email: String) -> Self { SideloaderBuilder { team_selection: None, @@ -90,6 +92,7 @@ impl SideloaderBuilder { machine_name: None, apple_email, max_certs_behavior: None, + delete_app_after_install: true, // extensions_behavior: None, } } @@ -128,6 +131,12 @@ impl SideloaderBuilder { self } + /// Set whether to delete the signed app from the temporary storage after installation. Defaults to `true`. + pub fn delete_app_after_install(mut self, delete: bool) -> Self { + self.delete_app_after_install = delete; + self + } + // pub fn extensions_behavior(mut self, behavior: ExtensionsBehavior) -> Self { // self.extensions_behavior = Some(behavior); // self @@ -145,6 +154,7 @@ impl SideloaderBuilder { .unwrap_or_else(|| Box::new(crate::util::storage::new_storage())), // self.extensions_behavior // .unwrap_or(ExtensionsBehavior::RegisterAll), + self.delete_app_after_install, ) } } diff --git a/isideload/src/sideload/sideloader.rs b/isideload/src/sideload/sideloader.rs index a1a0272..9e4f918 100644 --- a/isideload/src/sideload/sideloader.rs +++ b/isideload/src/sideload/sideloader.rs @@ -30,6 +30,7 @@ pub struct Sideloader { apple_email: String, max_certs_behavior: MaxCertsBehavior, //extensions_behavior: ExtensionsBehavior, + delete_app_after_install: bool, } impl Sideloader { @@ -44,6 +45,7 @@ impl Sideloader { machine_name: String, storage: Box, //extensions_behavior: ExtensionsBehavior, + delete_app_after_install: bool, ) -> Self { Sideloader { team_selection, @@ -53,10 +55,11 @@ impl Sideloader { apple_email, max_certs_behavior, //extensions_behavior, + delete_app_after_install, } } - /// Sign and install an app + /// Sign the app at the provided path and return the path to the signed app bundle (in a temp dir). To sign and install, see [`Self::install_app`]. pub async fn sign_app( &mut self, app_path: PathBuf, @@ -178,10 +181,12 @@ impl Sideloader { } #[cfg(feature = "install")] + /// Sign and install an app to a device. pub async fn install_app( &mut self, device_provider: &impl IdeviceProvider, app_path: PathBuf, + // this is gross but will be replaced with proper entitlement handling later increased_memory_limit: bool, ) -> Result, Report> { let device_info = IdeviceInfo::from_device(device_provider).await?; @@ -203,8 +208,15 @@ impl Sideloader { .await .context("Failed to install app on device")?; + if self.delete_app_after_install { + if let Err(e) = tokio::fs::remove_dir_all(signed_app_path).await { + tracing::warn!("Failed to remove temporary signed app file: {}", e); + }; + } + Ok(special_app) } + /// Get the developer team according to the configured team selection behavior pub async fn get_team(&mut self) -> Result { let teams = self.dev_session.list_teams().await?; diff --git a/isideload/src/util/storage.rs b/isideload/src/util/storage.rs index 9e09a5a..7b02db9 100644 --- a/isideload/src/util/storage.rs +++ b/isideload/src/util/storage.rs @@ -3,6 +3,7 @@ use std::{collections::HashMap, sync::Mutex}; use base64::prelude::*; use rootcause::prelude::*; +/// A trait for storing and retrieving sideloading related data, such as anisette state and certificates. pub trait SideloadingStorage: Send + Sync { fn store(&self, key: &str, value: &str) -> Result<(), Report>; fn retrieve(&self, key: &str) -> Result, Report>; @@ -24,6 +25,7 @@ pub trait SideloadingStorage: Send + Sync { } } +/// Factory function to create a new storage instance based on enabled features. The priority is `keyring-storage`, then `fs-storage`, and finally an in-memory storage if neither of those features are enabled. pub fn new_storage() -> impl SideloadingStorage { #[cfg(feature = "keyring-storage")] { From 6547688fa76c884bfbb86492fbaa907678dad860 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 14 Feb 2026 17:39:20 -0500 Subject: [PATCH 71/71] Finalize README --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 88d897c..4dd7e88 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,11 @@ A Rust library for sideloading iOS applications using an Apple ID. Used in [CrossCode](https://github.com/nab138/CrossCode) and [iloader](https://github.com/nab138/iloader). -This branch is home to isideload-next, the next major version of isideload. It features a redesigned API, improved error handling, better entitlement handling, and more. It is not ready! - ## Usage **You must call `isideload::init()` at the start of your program to ensure that errors are properly reported.** If you don't, errors related to network requests will not show any details. -A full usage example is available is in [examples/minimal](examples/minimal/). +A full example is available is in [examples/minimal](examples/minimal/). ## TODO