Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
152836fe54 | ||
|
|
507796887b | ||
|
|
2f08e5e16d |
2
.github/workflows/rust-quality.yml
vendored
2
.github/workflows/rust-quality.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- run: rustup component add clippy
|
||||
- name: Run clippy
|
||||
run : cargo clippy
|
||||
run : cargo clippy --all-features
|
||||
|
||||
fmt:
|
||||
name: "fmt"
|
||||
|
||||
@@ -9,7 +9,7 @@ homepage = "https://github.com/cocool97/adb_client"
|
||||
keywords = ["adb", "android", "tcp", "usb"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/cocool97/adb_client"
|
||||
version = "2.0.3"
|
||||
version = "2.0.5"
|
||||
|
||||
# To build locally when working on a new release
|
||||
[patch.crates-io]
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) [year] [fullname]
|
||||
Copyright (c) 2023-2024 Corentin LIAUD
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
<a href="https://deps.rs/repo/github/cocool97/adb_client">
|
||||
<img alt="dependency status" src="https://deps.rs/repo/github/cocool97/adb_client/status.svg"/>
|
||||
</a>
|
||||
<a href="https://opensource.org/licenses/MIT">
|
||||
<img alt="dependency status" src="https://img.shields.io/badge/License-MIT-yellow.svg"/>
|
||||
</a>
|
||||
</p>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ repository.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
adb_client = { version = "2.0.1" }
|
||||
adb_client = { version = "2.0.5" }
|
||||
anyhow = { version = "1.0.89" }
|
||||
clap = { version = "4.5.18", features = ["derive"] }
|
||||
env_logger = { version = "0.11.5" }
|
||||
|
||||
@@ -10,19 +10,21 @@ repository.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.22.1"
|
||||
bincode = "1.3.3"
|
||||
base64 = { version = "0.22.1" }
|
||||
bincode = { version = "1.3.3" }
|
||||
byteorder = { version = "1.5.0" }
|
||||
chrono = { version = "0.4.38" }
|
||||
homedir = { version = "0.3.4" }
|
||||
image = { version = "0.25.4" }
|
||||
lazy_static = { version = "1.5.0" }
|
||||
log = { version = "0.4.22" }
|
||||
num-bigint = { version = "0.6", package = "num-bigint-dig" }
|
||||
rand = { version = "0.7.0" }
|
||||
num-bigint = { version = "0.8.4", package = "num-bigint-dig" }
|
||||
num-traits = { version = "0.2.19" }
|
||||
rand = { version = "0.8.5" }
|
||||
regex = { version = "1.11.0", features = ["perf", "std", "unicode"] }
|
||||
rsa = { version = "0.3.0" }
|
||||
rsa = { version = "0.9.6" }
|
||||
rusb = { version = "0.9.4", features = ["vendored"] }
|
||||
serde = { version = "1.0.210", features = ["derive"] }
|
||||
serde_repr = "0.1.19"
|
||||
serde_repr = { version = "0.1.19" }
|
||||
sha1 = { version = "0.10.6", features = ["oid"] }
|
||||
thiserror = { version = "2.0.1" }
|
||||
|
||||
@@ -90,4 +90,7 @@ pub enum RustADBError {
|
||||
/// Given path does not represent an APK
|
||||
#[error("wrong file extension: {0}")]
|
||||
WrongFileExtension(String),
|
||||
/// An error occurred with PKCS8 data
|
||||
#[error("error with pkcs8: {0}")]
|
||||
RsaPkcs8Error(#[from] rsa::pkcs8::Error),
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ mod utils;
|
||||
|
||||
pub use adb_device_ext::ADBDeviceExt;
|
||||
pub use error::{Result, RustADBError};
|
||||
pub use models::{AdbVersion, DeviceLong, DeviceShort, DeviceState, RebootType};
|
||||
pub use models::{AdbStatResponse, AdbVersion, DeviceLong, DeviceShort, DeviceState, RebootType};
|
||||
pub use server::*;
|
||||
pub use transports::*;
|
||||
pub use usb::ADBUSBDevice;
|
||||
|
||||
@@ -8,10 +8,14 @@ use std::{
|
||||
use byteorder::LittleEndian;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Represents a `stat` response
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct AdbStatResponse {
|
||||
/// File permissions
|
||||
pub file_perm: u32,
|
||||
/// File size, in bytes
|
||||
pub file_size: u32,
|
||||
/// File modification time
|
||||
pub mod_time: u32,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
use std::{io::Read, iter::Map, path::Path, slice::ChunksExact};
|
||||
use std::{
|
||||
io::{Read, Seek, Write},
|
||||
iter::Map,
|
||||
path::Path,
|
||||
slice::ChunksExact,
|
||||
};
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use image::{ImageBuffer, Rgba};
|
||||
use image::{ImageBuffer, ImageFormat, Rgba};
|
||||
|
||||
use crate::{models::AdbServerCommand, utils, ADBServerDevice, Result, RustADBError};
|
||||
|
||||
@@ -98,13 +103,21 @@ impl TryFrom<[u8; std::mem::size_of::<Self>()]> for FrameBufferInfoV2 {
|
||||
}
|
||||
|
||||
impl ADBServerDevice {
|
||||
/// Dump framebuffer of this device
|
||||
/// Dump framebuffer of this device into given ['path']
|
||||
/// Big help from source code (https://android.googlesource.com/platform/system/adb/+/refs/heads/main/framebuffer_service.cpp)
|
||||
pub fn framebuffer<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
|
||||
let img = self.framebuffer_inner()?;
|
||||
Ok(img.save(path.as_ref())?)
|
||||
}
|
||||
|
||||
/// Dump framebuffer of this device and return corresponding bytes.
|
||||
///
|
||||
/// Output data format is currently only `PNG`.
|
||||
pub fn framebuffer_bytes<W: Write + Seek>(&mut self, mut writer: W) -> Result<()> {
|
||||
let img = self.framebuffer_inner()?;
|
||||
Ok(img.write_to(&mut writer, ImageFormat::Png)?)
|
||||
}
|
||||
|
||||
/// Inner method requesting framebuffer from Android device
|
||||
fn framebuffer_inner(&mut self) -> Result<ImageBuffer<Rgba<u8>, Vec<u8>>> {
|
||||
let serial: String = self.identifier.clone();
|
||||
|
||||
@@ -1,93 +1,114 @@
|
||||
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 num_bigint::{BigUint, ModInverse};
|
||||
use num_traits::cast::ToPrimitive;
|
||||
use num_traits::FromPrimitive;
|
||||
use rand::rngs::OsRng;
|
||||
use rsa::{Hash, PaddingScheme, PublicKeyParts, RSAPrivateKey};
|
||||
use std::convert::TryInto;
|
||||
use rsa::pkcs8::DecodePrivateKey;
|
||||
use rsa::traits::PublicKeyParts;
|
||||
use rsa::{Pkcs1v15Sign, RsaPrivateKey};
|
||||
|
||||
// From project: https://github.com/hajifkd/webadb
|
||||
#[derive(Debug)]
|
||||
pub struct ADBRsaKey {
|
||||
private_key: RSAPrivateKey,
|
||||
const ADB_PRIVATE_KEY_SIZE: usize = 2048;
|
||||
const ANDROID_PUBKEY_MODULUS_SIZE_WORDS: u32 = 64;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default)]
|
||||
/// Internal ADB representation of a public key
|
||||
struct ADBRsaInternalPublicKey {
|
||||
pub modulus_size_words: u32,
|
||||
pub n0inv: u32,
|
||||
pub modulus: BigUint,
|
||||
pub rr: Vec<u8>,
|
||||
pub exponent: u32,
|
||||
}
|
||||
|
||||
impl ADBRsaKey {
|
||||
pub fn random_with_size(size: usize) -> Result<Self> {
|
||||
let mut rng = OsRng;
|
||||
impl ADBRsaInternalPublicKey {
|
||||
pub fn new(exponent: &BigUint, modulus: &BigUint) -> Result<Self> {
|
||||
Ok(Self {
|
||||
private_key: RSAPrivateKey::new(&mut rng, size)?,
|
||||
modulus_size_words: ANDROID_PUBKEY_MODULUS_SIZE_WORDS,
|
||||
exponent: exponent.to_u32().ok_or(RustADBError::ConversionError)?,
|
||||
modulus: modulus.clone(),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_pkcs8(pkcs8_content: &str) -> Result<Self> {
|
||||
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)?;
|
||||
pub fn into_bytes(mut self) -> Vec<u8> {
|
||||
let mut bytes: Vec<u8> = Vec::new();
|
||||
bytes.append(&mut self.modulus_size_words.to_le_bytes().to_vec());
|
||||
bytes.append(&mut self.n0inv.to_le_bytes().to_vec());
|
||||
bytes.append(&mut self.modulus.to_bytes_le());
|
||||
bytes.append(&mut self.rr);
|
||||
bytes.append(&mut self.exponent.to_le_bytes().to_vec());
|
||||
|
||||
Ok(ADBRsaKey { private_key })
|
||||
}
|
||||
|
||||
pub fn encoded_public_key(&self) -> Result<String> {
|
||||
// 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::<LittleEndian>(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().ok_or(RustADBError::ConversionError)?;
|
||||
let n0inv_p: u32 = 1 + !u32::from_le_bytes((&n0inv.to_bytes_le()[..4]).try_into()?);
|
||||
result.write_u32::<LittleEndian>(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<Vec<u8>> {
|
||||
Ok(self.private_key.sign(
|
||||
PaddingScheme::new_pkcs1v15_sign(Some(Hash::SHA1)),
|
||||
msg.as_ref(),
|
||||
)?)
|
||||
bytes
|
||||
}
|
||||
}
|
||||
|
||||
fn write_biguint(writer: &mut Vec<u8>, data: &BigUint, n_bytes: usize) -> Result<()> {
|
||||
for &v in data
|
||||
.to_bytes_le()
|
||||
.iter()
|
||||
.chain(std::iter::repeat(&0))
|
||||
.take(n_bytes)
|
||||
{
|
||||
writer.write_u8(v)?;
|
||||
#[derive(Debug)]
|
||||
pub struct ADBRsaKey {
|
||||
private_key: RsaPrivateKey,
|
||||
}
|
||||
|
||||
impl ADBRsaKey {
|
||||
pub fn new_random() -> Result<Self> {
|
||||
Ok(Self {
|
||||
private_key: RsaPrivateKey::new(&mut OsRng, ADB_PRIVATE_KEY_SIZE)?,
|
||||
})
|
||||
}
|
||||
|
||||
Ok(())
|
||||
pub fn new_from_pkcs8(pkcs8_content: &str) -> Result<Self> {
|
||||
Ok(ADBRsaKey {
|
||||
private_key: RsaPrivateKey::from_pkcs8_pem(pkcs8_content)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn android_pubkey_encode(&self) -> Result<String> {
|
||||
// Helped from project: https://github.com/hajifkd/webadb
|
||||
// Source code: https://android.googlesource.com/platform/system/core/+/refs/heads/main/libcrypto_utils/android_pubkey.cpp
|
||||
// Useful function `android_pubkey_encode()`
|
||||
let mut adb_rsa_pubkey =
|
||||
ADBRsaInternalPublicKey::new(self.private_key.e(), self.private_key.n())?;
|
||||
|
||||
// r32 = 2 ^ 32
|
||||
let r32 = BigUint::from_u64(1 << 32).ok_or(RustADBError::ConversionError)?;
|
||||
|
||||
// r = 2 ^ rsa_size = 2 ^ 2048
|
||||
let r = set_bit(ADB_PRIVATE_KEY_SIZE)?;
|
||||
|
||||
// rr = r ^ 2 mod N
|
||||
let rr = r.modpow(&BigUint::from(2u32), &adb_rsa_pubkey.modulus);
|
||||
adb_rsa_pubkey.rr = rr.to_bytes_le();
|
||||
|
||||
// rem = N[0]
|
||||
let rem = &adb_rsa_pubkey.modulus % &r32;
|
||||
|
||||
// n0inv = -1 / rem mod r32
|
||||
let n0inv = rem
|
||||
.mod_inverse(&r32)
|
||||
.and_then(|v| v.to_biguint())
|
||||
.ok_or(RustADBError::ConversionError)?;
|
||||
|
||||
// BN_sub(n0inv, r32, n0inv)
|
||||
adb_rsa_pubkey.n0inv = (r32 - n0inv)
|
||||
.to_u32()
|
||||
.ok_or(RustADBError::ConversionError)?;
|
||||
|
||||
Ok(self.encode_public_key(adb_rsa_pubkey.into_bytes()))
|
||||
}
|
||||
|
||||
fn encode_public_key(&self, pub_key: Vec<u8>) -> String {
|
||||
let mut encoded = STANDARD.encode(pub_key);
|
||||
encoded.push(' ');
|
||||
encoded.push_str(&format!("adb_client@{}", env!("CARGO_PKG_VERSION")));
|
||||
|
||||
encoded
|
||||
}
|
||||
|
||||
pub fn sign(&self, msg: impl AsRef<[u8]>) -> Result<Vec<u8>> {
|
||||
Ok(self
|
||||
.private_key
|
||||
.sign(Pkcs1v15Sign::new::<sha1::Sha1>(), msg.as_ref())?)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_bit(n: usize) -> Result<BigUint> {
|
||||
@@ -133,9 +154,9 @@ CNkECiepaGyquQaffwR1CAi8dH6biJjlTQWQPFcCLA0hvernWo3eaSfiL7fHyym+
|
||||
ile69MHFENUePSpuRSiF3Z02
|
||||
-----END PRIVATE KEY-----";
|
||||
let priv_key =
|
||||
ADBRsaKey::from_pkcs8(DEFAULT_PRIV_KEY).expect("cannot create rsa key from data");
|
||||
ADBRsaKey::new_from_pkcs8(DEFAULT_PRIV_KEY).expect("cannot create rsa key from data");
|
||||
let pub_key = priv_key
|
||||
.encoded_public_key()
|
||||
.android_pubkey_encode()
|
||||
.expect("cannot encode public key");
|
||||
let pub_key_adb = "\
|
||||
QAAAAFH/pU9PVrHRgEjMGnpvOr2QzKYCavSE1fcSwvpS1uPn9GTmuyZr7c9up\
|
||||
|
||||
@@ -32,7 +32,7 @@ pub struct ADBUSBDevice {
|
||||
fn read_adb_private_key<P: AsRef<Path>>(private_key_path: P) -> Result<Option<ADBRsaKey>> {
|
||||
read_to_string(private_key_path.as_ref())
|
||||
.map_err(RustADBError::from)
|
||||
.map(|pk| match ADBRsaKey::from_pkcs8(&pk) {
|
||||
.map(|pk| match ADBRsaKey::new_from_pkcs8(&pk) {
|
||||
Ok(pk) => Some(pk),
|
||||
Err(e) => {
|
||||
log::error!("Error while create RSA private key: {e}");
|
||||
@@ -121,7 +121,7 @@ impl ADBUSBDevice {
|
||||
) -> Result<Self> {
|
||||
let private_key = match read_adb_private_key(private_key_path)? {
|
||||
Some(pk) => pk,
|
||||
None => ADBRsaKey::random_with_size(2048)?,
|
||||
None => ADBRsaKey::new_random()?,
|
||||
};
|
||||
|
||||
let mut s = Self {
|
||||
@@ -202,7 +202,7 @@ impl ADBUSBDevice {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut pubkey = self.private_key.encoded_public_key()?.into_bytes();
|
||||
let mut pubkey = self.private_key.android_pubkey_encode()?.into_bytes();
|
||||
pubkey.push(b'\0');
|
||||
|
||||
let message = ADBUsbMessage::new(USBCommand::Auth, AUTH_RSAPUBLICKEY, 0, pubkey);
|
||||
|
||||
Reference in New Issue
Block a user