Magisk/native/src/core/cert.rs

143 lines
4.8 KiB
Rust
Raw Normal View History

use std::fs::File;
use std::io;
2023-06-20 00:19:40 -07:00
use std::io::{Cursor, Read, Seek, SeekFrom};
use std::os::fd::{FromRawFd, RawFd};
use base::*;
const EOCD_MAGIC: u32 = 0x06054B50;
const APK_SIGNING_BLOCK_MAGIC: [u8; 16] = *b"APK Sig Block 42";
const SIGNATURE_SCHEME_V2_MAGIC: u32 = 0x7109871A;
macro_rules! bad_apk {
($msg:literal) => {
io::Error::new(io::ErrorKind::InvalidData, concat!("cert: ", $msg))
};
}
/*
* A v2/v3 signed APK has the format as following
*
* +---------------+
* | zip content |
* +---------------+
* | signing block |
* +---------------+
* | central dir |
* +---------------+
* | EOCD |
* +---------------+
*
* Scan from end of file to find EOCD, and figure our way back to the
* offset of the signing block. Next, directly extract the certificate
* from the v2 signature block.
*
* All structures above are mostly just for documentation purpose.
*
* This method extracts the first certificate of the first signer
* within the APK v2 signature block.
*/
pub fn read_certificate(fd: RawFd, version: i32) -> Vec<u8> {
2023-06-20 00:19:40 -07:00
fn inner(apk: &mut File, version: i32) -> io::Result<Vec<u8>> {
let mut u32_val = 0u32;
let mut u64_val = 0u64;
// Find EOCD
for i in 0u16.. {
let mut comment_sz = 0u16;
apk.seek(SeekFrom::End(-(comment_sz.bytes_size() as i64) - i as i64))?;
2023-06-20 00:19:40 -07:00
apk.read_flat_data(&mut comment_sz)?;
if comment_sz == i {
apk.seek(SeekFrom::Current(-22))?;
let mut magic = 0u32;
2023-06-20 00:19:40 -07:00
apk.read_flat_data(&mut magic)?;
if magic == EOCD_MAGIC {
break;
}
}
if i == 0xffff {
return Err(bad_apk!("invalid APK format"));
}
}
// We are now at EOCD + sizeof(magic)
// Seek and read central_dir_off to find the start of the central directory
let mut central_dir_off = 0u32;
apk.seek(SeekFrom::Current(12))?;
2023-06-20 00:19:40 -07:00
apk.read_flat_data(&mut central_dir_off)?;
// Code for parse APK comment to get version code
if version >= 0 {
let mut comment_sz = 0u16;
2023-06-20 00:19:40 -07:00
apk.read_flat_data(&mut comment_sz)?;
let mut comment = vec![0u8; comment_sz as usize];
apk.read_exact(&mut comment)?;
2023-06-20 00:19:40 -07:00
let mut comment = Cursor::new(&comment);
let mut apk_ver = 0;
comment.foreach_props(|k, v| {
if k == "versionCode" {
2023-06-20 00:19:40 -07:00
apk_ver = v.parse::<i32>().unwrap_or(0);
false
2023-06-16 01:40:05 -07:00
} else {
true
}
});
if version > apk_ver {
return Err(bad_apk!("APK version too low"));
}
}
// Next, find the start of the APK signing block
apk.seek(SeekFrom::Start((central_dir_off - 24) as u64))?;
2023-06-20 00:19:40 -07:00
apk.read_flat_data(&mut u64_val)?; // u64_value = block_sz_
let mut magic = [0u8; 16];
2023-06-20 00:19:40 -07:00
apk.read_exact(&mut magic)?;
if magic != APK_SIGNING_BLOCK_MAGIC {
return Err(bad_apk!("invalid signing block magic"));
}
let mut signing_blk_sz = 0u64;
apk.seek(SeekFrom::Current(
2023-06-20 00:19:40 -07:00
-(u64_val as i64) - (signing_blk_sz.bytes_size() as i64),
))?;
2023-06-20 00:19:40 -07:00
apk.read_flat_data(&mut signing_blk_sz)?;
if signing_blk_sz != u64_val {
return Err(bad_apk!("invalid signing block size"));
}
// Finally, we are now at the beginning of the id-value pair sequence
loop {
2023-06-20 00:19:40 -07:00
apk.read_flat_data(&mut u64_val)?; // id-value pair length
if u64_val == signing_blk_sz {
break;
}
let mut id = 0u32;
2023-06-20 00:19:40 -07:00
apk.read_flat_data(&mut id)?;
if id == SIGNATURE_SCHEME_V2_MAGIC {
// Skip [signer sequence length] + [1st signer length] + [signed data length]
2023-06-20 00:19:40 -07:00
apk.seek(SeekFrom::Current((u32_val.bytes_size() * 3) as i64))?;
2023-06-20 00:19:40 -07:00
apk.read_flat_data(&mut u32_val)?; // digest sequence length
apk.seek(SeekFrom::Current(u32_val as i64))?; // skip all digests
2023-06-20 00:19:40 -07:00
apk.seek(SeekFrom::Current(u32_val.bytes_size() as i64))?; // cert sequence length
apk.read_flat_data(&mut u32_val)?; // 1st cert length
2023-06-20 00:19:40 -07:00
let mut cert = vec![0; u32_val as usize];
apk.read_exact(cert.as_mut())?;
return Ok(cert);
} else {
// Skip this id-value pair
2023-06-20 00:19:40 -07:00
apk.seek(SeekFrom::Current(u64_val as i64 - (id.bytes_size() as i64)))?;
}
}
Err(bad_apk!("cannot find certificate"))
}
2023-06-20 00:19:40 -07:00
let mut file = unsafe { File::from_raw_fd(fd) };
let r = inner(&mut file, version).log().unwrap_or(vec![]);
std::mem::forget(file);
r
}