Add OpenSSL dependency

This commit is contained in:
Jackson Coxson
2025-11-15 11:45:34 -07:00
parent 13c5b48b1c
commit db894120da
6 changed files with 233 additions and 52 deletions

View File

@@ -12,16 +12,18 @@ keywords = ["lockdownd", "ios"]
[dependencies]
tokio = { version = "1", features = ["io-util"] }
tokio-rustls = { version = "0.26", default-features = false }
tokio-rustls = { version = "0.26", default-features = false, optional = true }
rustls = { version = "0.23", default-features = false, features = [
"std",
"tls12",
] }
crossfire = { version = "2.1", optional = true }
], optional = true }
tokio-openssl = { version = "0.6", optional = true }
openssl = { version = "0.10", optional = true }
plist = { version = "1.8" }
serde = { version = "1", features = ["derive"] }
ns-keyed-archive = { version = "0.1.4", optional = true }
crossfire = { version = "2.1", optional = true }
thiserror = { version = "2" }
tracing = { version = "0.1.41" }
@@ -64,8 +66,10 @@ bytes = "1.10.1"
[features]
default = ["aws-lc"]
aws-lc = ["rustls/aws-lc-rs", "tokio-rustls/aws-lc-rs"]
ring = ["rustls/ring", "tokio-rustls/ring"]
aws-lc = ["rustls", "rustls/aws-lc-rs", "tokio-rustls/aws-lc-rs"]
ring = ["rustls", "rustls/ring", "tokio-rustls/ring"]
rustls = ["dep:rustls", "dep:tokio-rustls"]
openssl = ["dep:openssl", "dep:tokio-openssl"]
afc = ["dep:chrono"]
amfi = []

View File

@@ -1,11 +1,12 @@
#![doc = include_str!("../README.md")]
// Jackson Coxson
#[cfg(feature = "pair")]
#[cfg(all(feature = "pair", feature = "rustls"))]
mod ca;
pub mod pairing_file;
pub mod plist_macro;
pub mod provider;
#[cfg(feature = "rustls")]
mod sni;
#[cfg(feature = "tunnel_tcp_stack")]
pub mod tcp;
@@ -27,6 +28,7 @@ pub use services::*;
pub use xpc::RemoteXpcClient;
use provider::{IdeviceProvider, RsdProvider};
#[cfg(feature = "rustls")]
use rustls::{crypto::CryptoProvider, pki_types::ServerName};
use std::{
io::{self, BufWriter},
@@ -460,57 +462,84 @@ impl Idevice {
&mut self,
pairing_file: &pairing_file::PairingFile,
) -> Result<(), IdeviceError> {
if CryptoProvider::get_default().is_none() {
// rust-analyzer will choke on this block, don't worry about it
let crypto_provider: CryptoProvider = {
#[cfg(all(feature = "ring", not(feature = "aws-lc")))]
{
debug!("Using ring crypto backend");
rustls::crypto::ring::default_provider()
#[cfg(feature = "rustls")]
{
if CryptoProvider::get_default().is_none() {
// rust-analyzer will choke on this block, don't worry about it
let crypto_provider: CryptoProvider = {
#[cfg(all(feature = "ring", not(feature = "aws-lc")))]
{
debug!("Using ring crypto backend");
rustls::crypto::ring::default_provider()
}
#[cfg(all(feature = "aws-lc", not(feature = "ring")))]
{
debug!("Using aws-lc crypto backend");
rustls::crypto::aws_lc_rs::default_provider()
}
#[cfg(not(any(feature = "ring", feature = "aws-lc")))]
{
compile_error!(
"No crypto backend was selected! Specify an idevice feature for a crypto backend"
);
}
#[cfg(all(feature = "ring", feature = "aws-lc"))]
{
// We can't throw a compile error because it breaks rust-analyzer.
// My sanity while debugging the workspace crates are more important.
debug!("Using ring crypto backend, because both were passed");
tracing::warn!(
"Both ring && aws-lc are selected as idevice crypto backends!"
);
rustls::crypto::ring::default_provider()
}
};
if let Err(e) = CryptoProvider::install_default(crypto_provider) {
// For whatever reason, getting the default provider will return None on iOS at
// random. Installing the default provider a second time will return an error, so
// we will log it but not propogate it. An issue should be opened with rustls.
tracing::error!("Failed to set crypto provider: {e:?}");
}
#[cfg(all(feature = "aws-lc", not(feature = "ring")))]
{
debug!("Using aws-lc crypto backend");
rustls::crypto::aws_lc_rs::default_provider()
}
#[cfg(not(any(feature = "ring", feature = "aws-lc")))]
{
compile_error!(
"No crypto backend was selected! Specify an idevice feature for a crypto backend"
);
}
#[cfg(all(feature = "ring", feature = "aws-lc"))]
{
// We can't throw a compile error because it breaks rust-analyzer.
// My sanity while debugging the workspace crates are more important.
debug!("Using ring crypto backend, because both were passed");
tracing::warn!("Both ring && aws-lc are selected as idevice crypto backends!");
rustls::crypto::ring::default_provider()
}
};
if let Err(e) = CryptoProvider::install_default(crypto_provider) {
// For whatever reason, getting the default provider will return None on iOS at
// random. Installing the default provider a second time will return an error, so
// we will log it but not propogate it. An issue should be opened with rustls.
tracing::error!("Failed to set crypto provider: {e:?}");
}
let config = sni::create_client_config(pairing_file)?;
let connector = tokio_rustls::TlsConnector::from(Arc::new(config));
let socket = self.socket.take().unwrap();
let socket = connector
.connect(ServerName::try_from("Device").unwrap(), socket)
.await?;
self.socket = Some(Box::new(socket));
Ok(())
}
let config = sni::create_client_config(pairing_file)?;
let connector = tokio_rustls::TlsConnector::from(Arc::new(config));
#[cfg(all(feature = "openssl", not(feature = "rustls")))]
{
let connector =
openssl::ssl::SslConnector::builder(openssl::ssl::SslMethod::tls()).unwrap();
let socket = self.socket.take().unwrap();
let socket = connector
.connect(ServerName::try_from("Device").unwrap(), socket)
.await?;
let mut connector = connector
.build()
.configure()
.unwrap()
.into_ssl("ur mom")
.unwrap();
self.socket = Some(Box::new(socket));
connector.set_certificate(&pairing_file.host_certificate)?;
connector.set_private_key(&pairing_file.host_private_key)?;
connector.set_verify(openssl::ssl::SslVerifyMode::empty());
let socket = self.socket.take().unwrap();
let mut ssl_stream = tokio_openssl::SslStream::new(connector, socket)?;
std::pin::Pin::new(&mut ssl_stream).connect().await?;
self.socket = Some(Box::new(ssl_stream));
Ok(())
Ok(())
}
}
}
@@ -521,12 +550,24 @@ impl Idevice {
pub enum IdeviceError {
#[error("device socket io failed")]
Socket(#[from] io::Error) = -1,
#[cfg(all(feature = "rustls", not(feature = "openssl")))]
#[error("PEM parse failed")]
PemParseFailed(#[from] rustls::pki_types::pem::Error) = -2,
#[cfg(all(feature = "rustls", not(feature = "openssl")))]
#[error("TLS error")]
Rustls(#[from] rustls::Error) = -3,
#[cfg(all(feature = "openssl", not(feature = "rustls")))]
#[error("TLS error")]
Rustls(#[from] openssl::ssl::Error) = -3,
#[cfg(all(feature = "rustls", not(feature = "openssl")))]
#[error("TLS verifiction build failed")]
TlsBuilderFailed(#[from] rustls::server::VerifierBuilderError) = -4,
#[cfg(all(feature = "openssl", not(feature = "rustls")))]
#[error("TLS verifiction build failed")]
TlsBuilderFailed(#[from] openssl::error::ErrorStack) = -4,
#[error("io on plist")]
Plist(#[from] plist::Error) = -5,
#[error("can't convert bytes to utf8")]

View File

@@ -5,7 +5,13 @@
use std::path::Path;
#[cfg(feature = "openssl")]
use openssl::{
pkey::{PKey, Private},
x509::X509,
};
use plist::Data;
#[cfg(feature = "rustls")]
use rustls::pki_types::{CertificateDer, pem::PemObject};
use serde::{Deserialize, Serialize};
use tracing::warn;
@@ -14,6 +20,7 @@ use tracing::warn;
///
/// Contains all cryptographic materials and identifiers needed for secure communication
/// with an iOS device, including certificates, private keys, and device identifiers.
#[cfg(feature = "rustls")]
#[derive(Clone, Debug)]
pub struct PairingFile {
/// Device's certificate in DER format
@@ -38,6 +45,21 @@ pub struct PairingFile {
pub udid: Option<String>,
}
#[cfg(all(feature = "openssl", not(feature = "rustls")))]
#[derive(Clone, Debug)]
pub struct PairingFile {
pub device_certificate: X509,
pub host_private_key: PKey<Private>,
pub host_certificate: X509,
pub root_private_key: PKey<Private>,
pub root_certificate: X509,
pub system_buid: String,
pub host_id: String,
pub escrow_bag: Vec<u8>,
pub wifi_mac_address: String,
pub udid: Option<String>,
}
/// Internal representation of a pairing file for serialization/deserialization
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "PascalCase")]
@@ -133,6 +155,7 @@ impl PairingFile {
///
/// # Errors
/// Returns `IdeviceError` if serialization fails
#[cfg(feature = "rustls")]
pub fn serialize(self) -> Result<Vec<u8>, crate::IdeviceError> {
let raw = RawPairingFile::from(self);
@@ -140,8 +163,18 @@ impl PairingFile {
plist::to_writer_xml(&mut buf, &raw)?;
Ok(buf)
}
#[cfg(all(feature = "openssl", not(feature = "rustls")))]
pub fn serialize(self) -> Result<Vec<u8>, crate::IdeviceError> {
let raw = RawPairingFile::try_from(self)?;
let mut buf = Vec::new();
plist::to_writer_xml(&mut buf, &raw)?;
Ok(buf)
}
}
#[cfg(feature = "rustls")]
impl TryFrom<RawPairingFile> for PairingFile {
type Error = rustls::pki_types::pem::Error;
@@ -180,6 +213,30 @@ impl TryFrom<RawPairingFile> for PairingFile {
}
}
#[cfg(all(feature = "openssl", not(feature = "rustls")))]
impl TryFrom<RawPairingFile> for PairingFile {
type Error = openssl::error::ErrorStack;
fn try_from(value: RawPairingFile) -> Result<Self, Self::Error> {
Ok(Self {
device_certificate: X509::from_pem(&Into::<Vec<u8>>::into(value.device_certificate))?,
host_private_key: PKey::private_key_from_pem(&Into::<Vec<u8>>::into(
value.host_private_key,
))?,
host_certificate: X509::from_pem(&Into::<Vec<u8>>::into(value.host_certificate))?,
root_private_key: PKey::private_key_from_pem(&Into::<Vec<u8>>::into(
value.root_private_key,
))?,
root_certificate: X509::from_pem(&Into::<Vec<u8>>::into(value.root_certificate))?,
system_buid: value.system_buid,
host_id: value.host_id,
escrow_bag: value.escrow_bag.into(),
wifi_mac_address: value.wifi_mac_address,
udid: value.udid,
})
}
}
impl From<PairingFile> for RawPairingFile {
/// Converts a structured pairing file into a raw pairing file for serialization
fn from(value: PairingFile) -> Self {

View File

@@ -243,7 +243,7 @@ impl LockdownClient {
///
/// # Errors
/// Returns `IdeviceError`
#[cfg(feature = "pair")]
#[cfg(all(feature = "pair", feature = "rustls"))]
pub async fn pair(
&mut self,
host_id: impl Into<String>,