From 5f7dc3744bf1d6236bb2f2db8f28ca78ab22a31e Mon Sep 17 00:00:00 2001 From: nab138 Date: Mon, 9 Feb 2026 17:53:17 -0500 Subject: [PATCH] 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())?); }