From 8d12e8b55148ab28631223aec9e63922ff229e21 Mon Sep 17 00:00:00 2001 From: LIAUD Corentin Date: Wed, 16 Oct 2024 17:38:08 +0200 Subject: [PATCH] feat: USB shell command, WIP for other commands --- adb_cli/src/main.rs | 3 +- adb_client/Cargo.toml | 5 +- adb_client/src/adb_device_ext.rs | 13 ++ adb_client/src/error.rs | 15 +- adb_client/src/lib.rs | 2 + .../src/server/adb_server_device_commands.rs | 54 +++++++ .../src/server/device_commands/logcat.rs | 2 +- .../src/server/device_commands/shell.rs | 47 ------ adb_client/src/server/mod.rs | 1 + adb_client/src/transports/usb_transport.rs | 44 ++++-- adb_client/src/usb/adb_rsa_key.rs | 145 ++++++++++++++++++ adb_client/src/usb/adb_usb_device.rs | 94 +++++------- adb_client/src/usb/adb_usb_device_commands.rs | 50 ++++++ adb_client/src/usb/adb_usb_message.rs | 39 +++-- adb_client/src/usb/mod.rs | 4 + adb_client/src/usb/usb_commands.rs | 34 ++-- 16 files changed, 394 insertions(+), 158 deletions(-) create mode 100644 adb_client/src/adb_device_ext.rs create mode 100644 adb_client/src/server/adb_server_device_commands.rs create mode 100644 adb_client/src/usb/adb_rsa_key.rs create mode 100644 adb_client/src/usb/adb_usb_device_commands.rs diff --git a/adb_cli/src/main.rs b/adb_cli/src/main.rs index 9b775f5..fd28bcd 100644 --- a/adb_cli/src/main.rs +++ b/adb_cli/src/main.rs @@ -6,7 +6,7 @@ mod adb_termios; mod commands; mod models; -use adb_client::{ADBEmulatorDevice, ADBServer, ADBUSBDevice, DeviceShort}; +use adb_client::{ADBDeviceExt, ADBEmulatorDevice, ADBServer, ADBUSBDevice, DeviceShort}; use anyhow::{anyhow, Result}; use clap::Parser; use commands::{EmuCommand, HostCommand, LocalCommand}; @@ -171,6 +171,7 @@ fn main() -> Result<()> { let mut device = ADBUSBDevice::new(usb.vendor_id, usb.product_id, usb.path_to_private_key)?; device.send_connect()?; + device.shell_command(["id"], std::io::stdout())?; } } diff --git a/adb_client/Cargo.toml b/adb_client/Cargo.toml index bbc96db..1d7a9c2 100644 --- a/adb_client/Cargo.toml +++ b/adb_client/Cargo.toml @@ -16,9 +16,8 @@ homedir = { version = "0.3.3" } image = { version = "0.25.2" } lazy_static = { version = "1.5.0" } log = { version = "0.4.22" } -rand = { version = "0.8.5" } +num-bigint = { version = "0.6", package = "num-bigint-dig" } regex = { version = "1.10.6", features = ["perf", "std", "unicode"] } -rsa = { version = "0.9.6", features = ["sha1"] } +rsa = { version = "0.3.0" } rusb = { version = "0.9.4", features = ["vendored"] } -sha1 = "0.10.6" thiserror = { version = "1.0.64" } diff --git a/adb_client/src/adb_device_ext.rs b/adb_client/src/adb_device_ext.rs new file mode 100644 index 0000000..4731c94 --- /dev/null +++ b/adb_client/src/adb_device_ext.rs @@ -0,0 +1,13 @@ +use std::io::Write; + +use crate::Result; + +/// Trait representing all features available on both [`ADBServerDevice`] and [`ADBUSBDevice`] +pub trait ADBDeviceExt { + /// Runs 'command' in a shell on the device, and write its output and error streams into [`output`]. + fn shell_command( + &mut self, + command: impl IntoIterator, + output: W, + ) -> Result<()>; +} diff --git a/adb_client/src/error.rs b/adb_client/src/error.rs index d67dc85..b01f6b7 100644 --- a/adb_client/src/error.rs +++ b/adb_client/src/error.rs @@ -69,16 +69,13 @@ pub enum RustADBError { /// CRC32 of the received message is invalid #[error("Invalid CRC32. Expected {0} got {1}")] InvalidCRC32(u32, u32), - /// An error occurred with RSA private key + /// Error while decoding base64 data #[error(transparent)] - RSAError(#[from] rsa::Error), - /// An error occurred with RSA PKCS#1 + Base64DecodeError(#[from] base64::DecodeError), + /// Error while encoding base64 data #[error(transparent)] - RSAPKCS1Error(#[from] rsa::pkcs1::Error), - /// An error occurred with RSA PKCS#8 + Base64EncodeError(#[from] base64::EncodeSliceError), + /// An error occurred with RSA engine #[error(transparent)] - RSAPKCS8Error(#[from] rsa::pkcs8::Error), - /// An error occurred with RSA signature - #[error(transparent)] - RSASignatureError(#[from] rsa::signature::Error), + RSAError(#[from] rsa::errors::Error), } diff --git a/adb_client/src/lib.rs b/adb_client/src/lib.rs index f24020b..ab8eca7 100644 --- a/adb_client/src/lib.rs +++ b/adb_client/src/lib.rs @@ -4,6 +4,7 @@ #![forbid(missing_docs)] #![doc = include_str!("../README.md")] +mod adb_device_ext; mod constants; mod emulator; mod error; @@ -13,6 +14,7 @@ mod transports; mod usb; mod utils; +pub use adb_device_ext::ADBDeviceExt; pub use error::{Result, RustADBError}; pub use models::{AdbVersion, DeviceLong, DeviceShort, DeviceState, RebootType}; pub use server::*; diff --git a/adb_client/src/server/adb_server_device_commands.rs b/adb_client/src/server/adb_server_device_commands.rs new file mode 100644 index 0000000..18ebd4e --- /dev/null +++ b/adb_client/src/server/adb_server_device_commands.rs @@ -0,0 +1,54 @@ +use std::io::{Read, Write}; + +use crate::{ + models::{AdbServerCommand, HostFeatures}, + ADBDeviceExt, ADBServerDevice, Result, RustADBError, +}; + +impl ADBDeviceExt for ADBServerDevice { + fn shell_command( + &mut self, + command: impl IntoIterator, + mut output: W, + ) -> Result<()> { + let supported_features = self.host_features()?; + if !supported_features.contains(&HostFeatures::ShellV2) + && !supported_features.contains(&HostFeatures::Cmd) + { + return Err(RustADBError::ADBShellNotSupported); + } + + let serial = self.identifier.clone(); + self.connect()? + .send_adb_request(AdbServerCommand::TransportSerial(serial))?; + self.get_transport_mut() + .send_adb_request(AdbServerCommand::ShellCommand( + command + .into_iter() + .map(|v| v.to_string()) + .collect::>() + .join(" "), + ))?; + + const BUFFER_SIZE: usize = 4096; + loop { + let mut buffer = [0; BUFFER_SIZE]; + match self + .get_transport_mut() + .get_raw_connection()? + .read(&mut buffer) + { + Ok(size) => { + if size == 0 { + return Ok(()); + } else { + output.write_all(&buffer[..size])?; + } + } + Err(e) => { + return Err(RustADBError::IOError(e)); + } + } + } + } +} diff --git a/adb_client/src/server/device_commands/logcat.rs b/adb_client/src/server/device_commands/logcat.rs index 2bf88ae..e39683e 100644 --- a/adb_client/src/server/device_commands/logcat.rs +++ b/adb_client/src/server/device_commands/logcat.rs @@ -1,6 +1,6 @@ use std::io::{self, Write}; -use crate::{ADBServerDevice, Result}; +use crate::{ADBDeviceExt, ADBServerDevice, Result}; struct LogFilter { writer: W, diff --git a/adb_client/src/server/device_commands/shell.rs b/adb_client/src/server/device_commands/shell.rs index d1ebc66..0b3ed8f 100644 --- a/adb_client/src/server/device_commands/shell.rs +++ b/adb_client/src/server/device_commands/shell.rs @@ -8,53 +8,6 @@ use crate::{ const BUFFER_SIZE: usize = 512; impl ADBServerDevice { - /// Runs 'command' in a shell on the device, and write its output and error streams into [`output`]. - pub fn shell_command( - &mut self, - command: impl IntoIterator, - mut output: W, - ) -> Result<()> { - let supported_features = self.host_features()?; - if !supported_features.contains(&HostFeatures::ShellV2) - && !supported_features.contains(&HostFeatures::Cmd) - { - return Err(RustADBError::ADBShellNotSupported); - } - - let serial = self.identifier.clone(); - self.connect()? - .send_adb_request(AdbServerCommand::TransportSerial(serial))?; - self.get_transport_mut() - .send_adb_request(AdbServerCommand::ShellCommand( - command - .into_iter() - .map(|v| v.to_string()) - .collect::>() - .join(" "), - ))?; - - const BUFFER_SIZE: usize = 4096; - loop { - let mut buffer = [0; BUFFER_SIZE]; - match self - .get_transport_mut() - .get_raw_connection()? - .read(&mut buffer) - { - Ok(size) => { - if size == 0 { - return Ok(()); - } else { - output.write_all(&buffer[..size])?; - } - } - Err(e) => { - return Err(RustADBError::IOError(e)); - } - } - } - } - /// Starts an interactive shell session on the device. /// Input data is read from [reader] and write to [writer]. /// [W] has a 'static bound as it is internally used in a thread. diff --git a/adb_client/src/server/mod.rs b/adb_client/src/server/mod.rs index 131e820..0ffbf24 100644 --- a/adb_client/src/server/mod.rs +++ b/adb_client/src/server/mod.rs @@ -1,6 +1,7 @@ mod adb_emulator_device; mod adb_server; mod adb_server_device; +mod adb_server_device_commands; mod device_commands; mod server_commands; diff --git a/adb_client/src/transports/usb_transport.rs b/adb_client/src/transports/usb_transport.rs index f7b1434..b8e6582 100644 --- a/adb_client/src/transports/usb_transport.rs +++ b/adb_client/src/transports/usb_transport.rs @@ -16,6 +16,9 @@ struct Endpoint { address: u8, } +const DEFAULT_READ_TIMEOUT: Duration = Duration::from_secs(2); +const DEFAULT_WRITE_TIMEOUT: Duration = Duration::from_secs(2); + /// Transport running on USB #[derive(Debug)] pub struct USBTransport { @@ -48,8 +51,17 @@ impl USBTransport { Ok(()) } - /// Write data to underlying connection + /// Write data to underlying connection, with default timeout pub(crate) fn write_message(&mut self, message: ADBUsbMessage) -> Result<()> { + self.write_message_with_timeout(message, DEFAULT_WRITE_TIMEOUT) + } + + /// Write data to underlying connection + pub(crate) fn write_message_with_timeout( + &mut self, + message: ADBUsbMessage, + timeout: Duration, + ) -> Result<()> { let endpoint = self.find_writable_endpoint()?; let handle = self.get_raw_connection()?; @@ -58,26 +70,25 @@ impl USBTransport { } Self::configure_endpoint(handle, &endpoint)?; - let max_timeout = Duration::from_secs(1); // TODO: loop let message_bytes = &message.to_bytes(); - let written = handle.write_bulk(endpoint.address, message_bytes, max_timeout)?; - println!("written {written}"); - - println!("writing payload..."); + let written = handle.write_bulk(endpoint.address, message_bytes, timeout)?; // TODO: loop let payload = message.into_payload(); - let written = handle.write_bulk(endpoint.address, &payload, max_timeout)?; - - println!("written {written}"); + let written = handle.write_bulk(endpoint.address, &payload, timeout)?; Ok(()) } - /// Read data from underlying connection + /// Read data from underlying connection with default timeout pub(crate) fn read_message(&mut self) -> Result { + self.read_message_with_timeout(DEFAULT_READ_TIMEOUT) + } + + /// Read data from underlying connection with given timeout + pub(crate) fn read_message_with_timeout(&mut self, timeout: Duration) -> Result { let endpoint = self.find_readable_endpoint()?; let handle = self.get_raw_connection()?; @@ -86,22 +97,21 @@ impl USBTransport { } Self::configure_endpoint(handle, &endpoint)?; - let max_timeout = Duration::from_secs(2); let mut data = [0; 24]; // TODO: loop - let read = handle.read_bulk(endpoint.address, &mut data, max_timeout)?; + let read = handle.read_bulk(endpoint.address, &mut data, timeout)?; let mut message = ADBUsbMessage::try_from(data)?; - if message.data_length != 0 { - let mut msg_data = vec![0_u8; message.data_length as usize]; + if message.data_length() != 0 { + let mut msg_data = vec![0_u8; message.data_length() as usize]; // TODO: loop - let read = handle.read_bulk(endpoint.address, &mut msg_data, max_timeout)?; - message.payload = msg_data; + let read = handle.read_bulk(endpoint.address, &mut msg_data, timeout)?; + message.with_payload(msg_data); } - println!("read {read} - {message:?}"); + log::trace!("read {message:?}"); Ok(message) } diff --git a/adb_client/src/usb/adb_rsa_key.rs b/adb_client/src/usb/adb_rsa_key.rs new file mode 100644 index 0000000..aa516cc --- /dev/null +++ b/adb_client/src/usb/adb_rsa_key.rs @@ -0,0 +1,145 @@ +use crate::{Result, RustADBError}; +use base64::{engine::general_purpose::STANDARD, Engine}; +use byteorder::{LittleEndian, WriteBytesExt}; +use num_bigint::traits::ModInverse; +use num_bigint::BigUint; +use rsa::{Hash, PaddingScheme, PublicKeyParts, RSAPrivateKey}; +use std::convert::TryInto; + +// From project: https://github.com/hajifkd/webadb +#[derive(Debug)] +pub struct ADBRsaKey { + pub private_key: RSAPrivateKey, +} + +impl ADBRsaKey { + pub fn from_pkcs8(pkcs8_content: &str) -> Result { + let der_encoded = pkcs8_content + .lines() + .filter(|line| !line.starts_with("-") && !line.is_empty()) + .fold(String::new(), |mut data, line| { + data.push_str(line); + data + }); + let der_bytes = STANDARD.decode(&der_encoded)?; + let private_key = RSAPrivateKey::from_pkcs8(&der_bytes)?; + + Ok(ADBRsaKey { private_key }) + } + + pub fn encoded_public_key(&self) -> Result { + // see https://android.googlesource.com/platform/system/core/+/android-4.4_r1/adb/adb_auth_host.c + // L63 RSA_to_RSAPublicKey + const RSANUMBYTES: u32 = 256; + const RSANUMWORDS: u32 = 64; + let user: String = format!("adb_client@{}", env!("CARGO_PKG_VERSION")); + + let mut result = vec![]; + result.write_u32::(RSANUMWORDS)?; + let r32 = set_bit(32); + let n = self.private_key.n(); + let r = set_bit((32 * RSANUMWORDS) as _); + // Well, let rr = set_bit((64 * RSANUMWORDS) as _) % n is also fine, since r \sim n. + let rr = r.modpow(&BigUint::from(2u32), n); + let rem = n % &r32; + let n0inv = rem.mod_inverse(&r32); + if let Some(n0inv) = n0inv { + let n0inv = n0inv.to_biguint().unwrap(); + let n0inv_p: u32 = + 1 + !u32::from_le_bytes((&n0inv.to_bytes_le()[..4]).try_into().unwrap()); + result.write_u32::(n0inv_p)?; + } else { + return Err(RustADBError::ConversionError); + } + + write_biguint(&mut result, n, RSANUMBYTES as _); + write_biguint(&mut result, &rr, RSANUMBYTES as _); + write_biguint(&mut result, self.private_key.e(), 4); + + let mut encoded = STANDARD.encode(&result); + encoded.push(' '); + encoded.push_str(&user); + Ok(encoded) + } + + pub fn sign(&self, msg: impl AsRef<[u8]>) -> Result> { + Ok(self.private_key.sign( + PaddingScheme::new_pkcs1v15_sign(Some(Hash::SHA1)), + msg.as_ref(), + )?) + } +} + +fn write_biguint(writer: &mut Vec, data: &BigUint, n_bytes: usize) { + for &v in data + .to_bytes_le() + .iter() + .chain(std::iter::repeat(&0)) + .take(n_bytes) + { + writer.write_u8(v).unwrap(); + } +} + +fn set_bit(n: usize) -> BigUint { + BigUint::parse_bytes( + &{ + let mut bits = vec![]; + bits.push(b'1'); + for _ in 0..n { + bits.push(b'0'); + } + bits + }[..], + 2, + ) + .unwrap() +} + +#[test] +fn test_pubkey_gen() { + const DEFAULT_PRIV_KEY: &'static str = r"-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC4Dyn85cxDJnjM +uYXQl/w469MDKdlGdviLfmFMWeYLVfL2Mz1AVyvKqscrtlhbbgMQ/M+3lDvEdHS0 +14RIGAwWRtrlTTmhLvM2/IO+eSKSYeCrCVc4KLG3E3WRryUXbs2ynA29xjTJVw+Z +xYxDyn/tAYPEyMm4v+HIJHcOtRzxtO2vjMJ2vBT/ywYxjhncXbFSO09q2E4XrHli +SIPyO82hZgCkpzTZRp+nyA17TYuV9++mvUr9lWH9RbC+o8EF3yitlBsE2uXr97EV +i2Qy8CE7FIxsihXlukppwKRuz+1rJrvmZPTn49ZS+sIS99WE9GoCpsyQvTpvehrM +SIDRsVZPAgMBAAECggEAWNXAzzXeS36zCSR1yILCknqHotw86Pyc4z7BGUe+dzQp +itiaNIaeNTgN3zQoGyDSzA0o+BLMcfo/JdVrHBy3IL1cAxYtvXTaoGxp7bGrlPk2 +pXZhqVJCy/jRYtokzdWF5DHbk/+pFJA3kGE/XKzM54g2n/DFI61A/QdUiz2w1ZtI +vc5cM08EM8B/TSI3SeWB8zkh5SlIuLsFO2J2+tCak6PdFfKOVIrFv9dKJYLxx+59 ++edZamw2EvNlnl/sewgUk0gaZvQKVf4ivHyM+KSHuV4RFfiLvGuVcyA6XhSjztsG +EA++jDHP5ib/Izes7UK09v9y7kow+z6vUtnDDQOvgQKBgQD8WWAn7FQt9aziCw19 +gZynzHG1bXI7uuEVSneuA3UwJImmDu8W+Qb9YL9Dc2nV0M5pGGdXKi2jzq8gPar6 +GPAmy7TOlov6Nm0pbMXTAfuovG+gIXxelp3US3FvyRupi0/7UQRRwvetFYbDFwJX +ydF5uEtZdGSHAjPeU5FLq6tBwQKBgQC6uN0JwwZn+eaxguyKOXvp0KykhFI0HI1A +MBDZ1uuKt6OW5+r9NeQtTLctGlNKVQ8wz+Wr0C/nLGIIv4lySS9WFyc5/FnFhDdy +LsEi6whcca4vq3jsMOukvQGFnERsou4LqBEI1Es7jjeeEq+/8WnNTi6Y1flZ6UAp +YAOeFI98DwKBgQDvyfHgHeajwZalOQF5qGb24AOQ9c4dyefGNnvhA/IgbCfMftZc +iwhETuGQM6R3A7KQFRtlrXOu+2BYD6Ffg8D37IwD3vRmL7+tJGoapwC/B0g+7nLi +4tZY+9Nv+LbrdbDry8GB+/UkKJdk3IFicCk4M5KOD1bTH5mwAtLHB/p1QQKBgDHi +k8M45GxA+p4wMUvYgb987bLiWyfq/N3KOaZJYhJkb4MwoLpXfIeRuFqHbvsr8GwF +DwIxE6s6U1KtAWaUIN5qPyOhxMYdRcbusNDIZCp2gKfhsuO/SiVwDYkJr8oqWVip +5SsrtJHLtBY6PdQVBkRAf/h7KiwYQfkL2suQCKmHAoGBAJAkYImBYPHuRcnSXikn +xGDK/moPvzs0CjdPlRcEN+Myy/G0FUrOaC0FcpNoJOdQSYz3F6URA4nX+zj6Ie7G +CNkECiepaGyquQaffwR1CAi8dH6biJjlTQWQPFcCLA0hvernWo3eaSfiL7fHyym+ +ile69MHFENUePSpuRSiF3Z02 +-----END PRIVATE KEY-----"; + let priv_key = ADBRsaKey::from_pkcs8(DEFAULT_PRIV_KEY).unwrap(); + let pub_key = priv_key.encoded_public_key().unwrap(); + let pub_key_adb = "\ +QAAAAFH/pU9PVrHRgEjMGnpvOr2QzKYCavSE1fcSwvpS1uPn9GTmuyZr7c9up\ +MBpSrrlFYpsjBQ7IfAyZIsVsffr5doEG5StKN8FwaO+sEX9YZX9Sr2m7/eVi0\ +17Dcinn0bZNKekAGahzTvyg0hieawXTthqTztSsV3cGY4xBsv/FLx2woyv7bT\ +xHLUOdyTI4b+4ycjEgwHtf8pDjMWZD1fJNMa9DZyyzW4XJa+RdRO3sSg4Vwmr\ +4GGSInm+g/w28y6hOU3l2kYWDBhIhNe0dHTEO5S3z/wQA25bWLYrx6rKK1dAP\ +TP28lUL5llMYX6L+HZG2SkD0+s4/JfQhbnMeCZDzOX8KQ+4ThLy/gDTqCSTjj\ +ic8BykdUIqYPwAjBMgQwLOLY5WNJMpjGlFINRcCGhvFFZ73sJTLerECuV/Oae\ +nFRcORwnGIRgMrYXj4tjmxJC7sq3cfNX96YIcSCDE9SZFdlKXVK8Jc4YMLGF3\ +MI8k1KoTby34uaIyxPJvwM1WR4Rdj60fwMyikFXNaOR2fPteZ3UMBA7CMrOEm\ +9iYjntyEppA4hQXIO1TWTzkA/Kfl1i67k5NuLIQdhPFEc5ox5IYVHusauPQ7d\ +Awu6BlgK37TUn0JdK0Z6Z4RaEIaNiEI0d5CoP6zQKV2QQnlscYpdsaUW5t9/F\ +LioVXPwrz0tx35JyIUZPPYwEAAQA= "; + assert_eq!(&pub_key[..pub_key_adb.len()], pub_key_adb); +} diff --git a/adb_client/src/usb/adb_usb_device.rs b/adb_client/src/usb/adb_usb_device.rs index b51a821..7391cbd 100644 --- a/adb_client/src/usb/adb_usb_device.rs +++ b/adb_client/src/usb/adb_usb_device.rs @@ -1,28 +1,19 @@ use std::fs::read_to_string; use std::path::PathBuf; +use std::time::Duration; -use super::ADBUsbMessage; +use super::{ADBRsaKey, ADBUsbMessage}; use crate::usb::adb_usb_message::{AUTH_RSAPUBLICKEY, AUTH_SIGNATURE, AUTH_TOKEN}; use crate::{usb::usb_commands::USBCommand, ADBTransport, Result, RustADBError, USBTransport}; -use base64::prelude::BASE64_STANDARD; -use base64::Engine; -use rsa::pkcs1::EncodeRsaPublicKey; -use rsa::signature::SignatureEncoding; -use rsa::signature::Signer; -use rsa::{pkcs1v15::SigningKey, pkcs8::DecodePrivateKey, RsaPrivateKey, RsaPublicKey}; -use sha1::Sha1; /// Represent a device reached directly over USB #[derive(Debug)] pub struct ADBUSBDevice { - // Raw bytes from the public key - public_key: Vec, - // Signing key derived from the private key for signing messages - signing_key: SigningKey, - transport: USBTransport, + private_key: ADBRsaKey, + pub(crate) transport: USBTransport, } -fn read_adb_private_key(private_key_path: Option) -> Option { +fn read_adb_private_key(private_key_path: Option) -> Option { let private_key = private_key_path.or_else(|| { homedir::my_home() .ok()? @@ -31,32 +22,20 @@ fn read_adb_private_key(private_key_path: Option) -> Option Result { - log::info!("generating ephemeral RSA keypair"); - let mut rng = rand::thread_rng(); - Ok(RsaPrivateKey::new(&mut rng, 2048)?) -} - impl ADBUSBDevice { /// Instantiate a new [ADBUSBDevice] pub fn new(vendor_id: u16, product_id: u16, private_key_path: Option) -> Result { let private_key = match read_adb_private_key(private_key_path) { Some(pk) => pk, - None => generate_keypair()?, + None => unimplemented!(), }; - let der_public_key = RsaPublicKey::from(&private_key).to_pkcs1_der()?; - let mut public_key = BASE64_STANDARD.encode(der_public_key); - public_key.push('\0'); - - let signing_key = SigningKey::::new(private_key); Ok(Self { - public_key: public_key.into_bytes(), - signing_key, + private_key, transport: USBTransport::new(vendor_id, product_id), }) } @@ -65,15 +44,11 @@ impl ADBUSBDevice { pub fn send_connect(&mut self) -> Result<()> { self.transport.connect()?; - // TO MAKE IT WORKING - // WIRE USB DEVICE - // IN NON ROOT RUN PROG - let message = ADBUsbMessage::new( USBCommand::Cnxn, 0x01000000, 1048576, - "host::pc-portable\0".into(), + "host::pc-portable\0".as_bytes().to_vec(), ); self.transport.write_message(message)?; @@ -83,52 +58,57 @@ impl ADBUSBDevice { // At this point, we should have received either: // - an AUTH message with arg0 == 1 // - a CNXN message - let auth_message = match message.command { - USBCommand::Auth if message.arg0 == AUTH_TOKEN => message, - USBCommand::Auth if message.arg0 != AUTH_TOKEN => { + let auth_message = match message.command() { + USBCommand::Auth if message.arg0() == AUTH_TOKEN => message, + USBCommand::Auth if message.arg0() != AUTH_TOKEN => { return Err(RustADBError::ADBRequestFailed( "Received AUTH message with type != 1".into(), )) } - USBCommand::Cnxn => { - log::info!("Successfully authenticated on device !"); - return Ok(()); - } - _ => { + c => { return Err(RustADBError::ADBRequestFailed(format!( "Wrong command received {}", - message.command + c ))) } }; - let signed_payload = self.signing_key.try_sign(&auth_message.payload)?; - let b = signed_payload.to_vec(); + let sign = self.private_key.sign(auth_message.into_payload()).unwrap(); + + let message = ADBUsbMessage::new(USBCommand::Auth, AUTH_SIGNATURE, 0, sign); - let message = ADBUsbMessage::new(USBCommand::Auth, AUTH_SIGNATURE, 0, b); self.transport.write_message(message)?; let received_response = self.transport.read_message()?; - if received_response.command == USBCommand::Cnxn { + if received_response.command() == USBCommand::Cnxn { log::info!("Successfully authenticated on device !"); return Ok(()); } - let message = ADBUsbMessage::new( - USBCommand::Auth, - AUTH_RSAPUBLICKEY, - 0, - // TODO: Make the function accept a slice of u8 - // to avoid clone - self.public_key.clone(), - ); + let mut pubkey = self.private_key.encoded_public_key().unwrap().into_bytes(); + pubkey.push(b'\0'); + + let message = ADBUsbMessage::new(USBCommand::Auth, AUTH_RSAPUBLICKEY, 0, pubkey); self.transport.write_message(message)?; - let response = self.transport.read_message()?; + let response = self + .transport + .read_message_with_timeout(Duration::from_secs(10))?; - dbg!(response); + match response.command() { + USBCommand::Cnxn => log::info!( + "Authentication OK, device info {}", + String::from_utf8(response.into_payload().to_vec()).unwrap() + ), + _ => { + return Err(RustADBError::ADBRequestFailed(format!( + "wrong response {}", + response.command() + ))) + } + } Ok(()) } diff --git a/adb_client/src/usb/adb_usb_device_commands.rs b/adb_client/src/usb/adb_usb_device_commands.rs new file mode 100644 index 0000000..eb0bc7c --- /dev/null +++ b/adb_client/src/usb/adb_usb_device_commands.rs @@ -0,0 +1,50 @@ +use std::io::Write; + +use crate::{ + usb::{ADBUsbMessage, USBCommand}, + ADBDeviceExt, ADBUSBDevice, Result, RustADBError, +}; + +impl ADBDeviceExt for ADBUSBDevice { + fn shell_command( + &mut self, + command: impl IntoIterator, + mut output: W, + ) -> Result<()> { + let message = ADBUsbMessage::new( + USBCommand::Open, + 1, + 0, + format!( + "shell:{}\0", + command + .into_iter() + .map(|v| v.to_string()) + .collect::>() + .join(" "), + ) + .as_bytes() + .to_vec(), + ); + self.transport.write_message(message)?; + + let response = self.transport.read_message()?; + if response.command() != USBCommand::Okay { + return Err(RustADBError::ADBRequestFailed(format!( + "wrong command {}", + response.command() + ))); + } + + loop { + let response = self.transport.read_message()?; + if response.command() != USBCommand::Write { + break; + } + + let write = output.write(&response.into_payload())?; + } + + Ok(()) + } +} diff --git a/adb_client/src/usb/adb_usb_message.rs b/adb_client/src/usb/adb_usb_message.rs index c9f3918..c1f3835 100644 --- a/adb_client/src/usb/adb_usb_message.rs +++ b/adb_client/src/usb/adb_usb_message.rs @@ -1,6 +1,5 @@ -use crate::RustADBError; - use super::usb_commands::USBCommand; +use crate::RustADBError; pub const AUTH_TOKEN: u32 = 1; pub const AUTH_SIGNATURE: u32 = 2; @@ -8,18 +7,18 @@ pub const AUTH_RSAPUBLICKEY: u32 = 3; #[derive(Debug)] pub struct ADBUsbMessage { - pub command: USBCommand, /* command identifier constant */ - pub arg0: u32, /* first argument */ - pub arg1: u32, /* second argument */ - pub data_length: u32, /* length of payload (0 is allowed) */ - pub data_crc32: u32, /* crc32 of data payload */ - pub magic: u32, /* command ^ 0xffffffff */ - pub payload: Vec, + command: USBCommand, /* command identifier constant */ + arg0: u32, /* first argument */ + arg1: u32, /* second argument */ + data_length: u32, /* length of payload (0 is allowed) */ + data_crc32: u32, /* crc32 of data payload */ + magic: u32, /* command ^ 0xffffffff */ + payload: Vec, } impl ADBUsbMessage { pub fn new(command: USBCommand, arg0: u32, arg1: u32, data: Vec) -> Self { - let command_u32 = command.to_u32(); + let command_u32 = command.u32_value(); Self { command, arg0, @@ -32,7 +31,7 @@ impl ADBUsbMessage { } pub fn compute_checksum(&self) -> u32 { - self.command.to_u32() ^ 0xFFFFFFFF + self.command.u32_value() ^ 0xFFFFFFFF } pub fn check_message_integrity(&self) -> bool { @@ -43,7 +42,7 @@ impl ADBUsbMessage { let mut result = [0u8; 24]; let mut offset = 0; - let command_bytes = self.command.to_u32().to_le_bytes(); + let command_bytes = self.command.u32_value().to_le_bytes(); result[offset..offset + 4].copy_from_slice(&command_bytes); offset += 4; @@ -69,9 +68,25 @@ impl ADBUsbMessage { result } + pub fn command(&self) -> USBCommand { + self.command + } + + pub fn arg0(&self) -> u32 { + self.arg0 + } + + pub fn data_length(&self) -> u32 { + self.data_length + } + pub fn into_payload(self) -> Vec { self.payload } + + pub fn with_payload(&mut self, payload: Vec) { + self.payload = payload; + } } impl TryFrom<[u8; 24]> for ADBUsbMessage { diff --git a/adb_client/src/usb/mod.rs b/adb_client/src/usb/mod.rs index 9ea69f7..54f25b4 100644 --- a/adb_client/src/usb/mod.rs +++ b/adb_client/src/usb/mod.rs @@ -1,6 +1,10 @@ +mod adb_rsa_key; mod adb_usb_device; +mod adb_usb_device_commands; mod adb_usb_message; mod usb_commands; + +pub use adb_rsa_key::ADBRsaKey; pub use adb_usb_device::ADBUSBDevice; pub use adb_usb_message::ADBUsbMessage; pub use usb_commands::USBCommand; diff --git a/adb_client/src/usb/usb_commands.rs b/adb_client/src/usb/usb_commands.rs index 990bb79..05efb40 100644 --- a/adb_client/src/usb/usb_commands.rs +++ b/adb_client/src/usb/usb_commands.rs @@ -2,27 +2,33 @@ use std::fmt::Display; use crate::RustADBError; -#[derive(Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum USBCommand { - // Connect to a device + /// Connect to a device Cnxn, - // Close connection to a device + /// Close connection to a device Clse, - // Device ask for authentication - Auth, // OTHERS - // A_SYNC 0x434e5953 - // A_OPEN 0x4e45504f - // A_OKAY 0x59414b4f - // A_WRTE 0x45545257 - // A_STLS 0x534C5453 + /// Device ask for authentication + Auth, + /// Open a data connection + Open, + /// Server understood the message + Okay, + /// Write data to connection + Write, + // Sync 0x434e5953 + // Stls 0x534C5453 } impl USBCommand { - pub fn to_u32(&self) -> u32 { + pub fn u32_value(&self) -> u32 { match self { Self::Cnxn => 0x4e584e43, Self::Clse => 0x45534c43, Self::Auth => 0x48545541, + Self::Open => 0x4e45504f, + Self::Write => 0x45545257, + Self::Okay => 0x59414b4f, } } } @@ -33,6 +39,9 @@ impl Display for USBCommand { USBCommand::Cnxn => write!(f, "CNXN"), USBCommand::Clse => write!(f, "CLSE"), USBCommand::Auth => write!(f, "AUTH"), + USBCommand::Open => write!(f, "OPEN"), + USBCommand::Write => write!(f, "WRTE"), + USBCommand::Okay => write!(f, "OKAY"), } } } @@ -45,6 +54,9 @@ impl TryFrom<&[u8]> for USBCommand { 0x4e584e43 => Ok(Self::Cnxn), 0x45534c43 => Ok(Self::Clse), 0x48545541 => Ok(Self::Auth), + 0x4e45504f => Ok(Self::Open), + 0x45545257 => Ok(Self::Write), + 0x59414b4f => Ok(Self::Okay), _ => Err(RustADBError::ConversionError), } }