Simplify magiskboot FFI

This commit is contained in:
topjohnwu
2025-08-20 22:25:19 -07:00
committed by John Wu
parent af51880a81
commit c313812129
10 changed files with 230 additions and 277 deletions

View File

@@ -87,7 +87,6 @@ LOCAL_STATIC_LIBRARIES := \
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
boot/bootimg.cpp \ boot/bootimg.cpp \
boot/format.cpp \
boot/boot-rs.cpp boot/boot-rs.cpp
LOCAL_LDFLAGS := -static LOCAL_LDFLAGS := -static

View File

@@ -48,6 +48,43 @@ static bool check_env(const char *name) {
return val != nullptr && val == "true"sv; return val != nullptr && val == "true"sv;
} }
FileFormat check_fmt(const void *buf, size_t len) {
if (CHECKED_MATCH(CHROMEOS_MAGIC)) {
return FileFormat::CHROMEOS;
} else if (CHECKED_MATCH(BOOT_MAGIC)) {
return FileFormat::AOSP;
} else if (CHECKED_MATCH(VENDOR_BOOT_MAGIC)) {
return FileFormat::AOSP_VENDOR;
} else if (CHECKED_MATCH(GZIP1_MAGIC) || CHECKED_MATCH(GZIP2_MAGIC)) {
return FileFormat::GZIP;
} else if (CHECKED_MATCH(LZOP_MAGIC)) {
return FileFormat::LZOP;
} else if (CHECKED_MATCH(XZ_MAGIC)) {
return FileFormat::XZ;
} else if (len >= 13 && memcmp(buf, "\x5d\x00\x00", 3) == 0
&& (((char *)buf)[12] == '\xff' || ((char *)buf)[12] == '\x00')) {
return FileFormat::LZMA;
} else if (CHECKED_MATCH(BZIP_MAGIC)) {
return FileFormat::BZIP2;
} else if (CHECKED_MATCH(LZ41_MAGIC) || CHECKED_MATCH(LZ42_MAGIC)) {
return FileFormat::LZ4;
} else if (CHECKED_MATCH(LZ4_LEG_MAGIC)) {
return FileFormat::LZ4_LEGACY;
} else if (CHECKED_MATCH(MTK_MAGIC)) {
return FileFormat::MTK;
} else if (CHECKED_MATCH(DTB_MAGIC)) {
return FileFormat::DTB;
} else if (CHECKED_MATCH(DHTB_MAGIC)) {
return FileFormat::DHTB;
} else if (CHECKED_MATCH(TEGRABLOB_MAGIC)) {
return FileFormat::BLOB;
} else if (len >= 0x28 && memcmp(&((char *)buf)[0x24], ZIMAGE_MAGIC, 4) == 0) {
return FileFormat::ZIMAGE;
} else {
return FileFormat::UNKNOWN;
}
}
void dyn_img_hdr::print() const { void dyn_img_hdr::print() const {
uint32_t ver = header_version(); uint32_t ver = header_version();
fprintf(stderr, "%-*s [%u]\n", PADDING, "HEADER_VER", ver); fprintf(stderr, "%-*s [%u]\n", PADDING, "HEADER_VER", ver);
@@ -472,7 +509,7 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
fprintf(stderr, "! Could not find zImage piggy, keeping raw kernel\n"); fprintf(stderr, "! Could not find zImage piggy, keeping raw kernel\n");
} }
} }
fprintf(stderr, "%-*s [%s]\n", PADDING, "KERNEL_FMT", fmt2name[k_fmt]); fprintf(stderr, "%-*s [%s]\n", PADDING, "KERNEL_FMT", fmt2name(k_fmt));
} }
if (auto size = hdr->ramdisk_size()) { if (auto size = hdr->ramdisk_size()) {
if (hdr->vendor_ramdisk_table_size()) { if (hdr->vendor_ramdisk_table_size()) {
@@ -493,7 +530,7 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
fprintf(stderr, fprintf(stderr,
"%-*s name=[%s] type=[%s] size=[%u] fmt=[%s]\n", PADDING, "VND_RAMDISK", "%-*s name=[%s] type=[%s] size=[%u] fmt=[%s]\n", PADDING, "VND_RAMDISK",
it.ramdisk_name, vendor_ramdisk_type(it.ramdisk_type), it.ramdisk_name, vendor_ramdisk_type(it.ramdisk_type),
it.ramdisk_size, fmt2name[fmt]); it.ramdisk_size, fmt2name(fmt));
} }
} else { } else {
r_fmt = check_fmt_lg(ramdisk, size); r_fmt = check_fmt_lg(ramdisk, size);
@@ -507,12 +544,12 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
hdr->ramdisk_size() -= sizeof(mtk_hdr); hdr->ramdisk_size() -= sizeof(mtk_hdr);
r_fmt = check_fmt_lg(ramdisk, hdr->ramdisk_size()); r_fmt = check_fmt_lg(ramdisk, hdr->ramdisk_size());
} }
fprintf(stderr, "%-*s [%s]\n", PADDING, "RAMDISK_FMT", fmt2name[r_fmt]); fprintf(stderr, "%-*s [%s]\n", PADDING, "RAMDISK_FMT", fmt2name(r_fmt));
} }
} }
if (auto size = hdr->extra_size()) { if (auto size = hdr->extra_size()) {
e_fmt = check_fmt_lg(extra, size); e_fmt = check_fmt_lg(extra, size);
fprintf(stderr, "%-*s [%s]\n", PADDING, "EXTRA_FMT", fmt2name[e_fmt]); fprintf(stderr, "%-*s [%s]\n", PADDING, "EXTRA_FMT", fmt2name(e_fmt));
} }
if (tail.sz()) { if (tail.sz()) {
@@ -523,8 +560,7 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
} else if (tail.sz() >= 16 && BUFFER_MATCH(tail.buf(), LG_BUMP_MAGIC)) { } else if (tail.sz() >= 16 && BUFFER_MATCH(tail.buf(), LG_BUMP_MAGIC)) {
fprintf(stderr, "LG_BUMP_IMAGE\n"); fprintf(stderr, "LG_BUMP_IMAGE\n");
flags[LG_BUMP_FLAG] = true; flags[LG_BUMP_FLAG] = true;
} else if (!(tail.sz() >= 4 && BUFFER_MATCH(tail.buf(), AVB_MAGIC)) && verify()) { } else if (verify()) {
// Check if the image is avb 1.0 signed
fprintf(stderr, "AVB1_SIGNED\n"); fprintf(stderr, "AVB1_SIGNED\n");
flags[AVB1_SIGNED_FLAG] = true; flags[AVB1_SIGNED_FLAG] = true;
} }
@@ -532,13 +568,13 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
// Find AVB footer // Find AVB footer
const void *footer = tail.buf() + tail.sz() - sizeof(AvbFooter); const void *footer = tail.buf() + tail.sz() - sizeof(AvbFooter);
if (BUFFER_MATCH(footer, AVB_FOOTER_MAGIC)) { if (BUFFER_MATCH(footer, AVB_FOOTER_MAGIC)) {
avb_footer = reinterpret_cast<const AvbFooter*>(footer); avb_footer = static_cast<const AvbFooter*>(footer);
// Double check if meta header exists // Double check if meta header exists
const void *meta = base_addr + __builtin_bswap64(avb_footer->vbmeta_offset); const void *meta = base_addr + __builtin_bswap64(avb_footer->vbmeta_offset);
if (BUFFER_MATCH(meta, AVB_MAGIC)) { if (BUFFER_MATCH(meta, AVB_MAGIC)) {
fprintf(stderr, "VBMETA\n"); fprintf(stderr, "VBMETA\n");
flags[AVB_FLAG] = true; flags[AVB_FLAG] = true;
vbmeta = reinterpret_cast<const AvbVBMetaImageHeader*>(meta); vbmeta = static_cast<const AvbVBMetaImageHeader*>(meta);
} }
} }
} }
@@ -547,10 +583,6 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
return true; return true;
} }
bool boot_img::verify(const char *cert) const {
return rust::verify_boot_image(*this, cert);
}
int split_image_dtb(rust::Utf8CStr filename, bool skip_decomp) { int split_image_dtb(rust::Utf8CStr filename, bool skip_decomp) {
mmap_data img(filename.data()); mmap_data img(filename.data());
@@ -801,7 +833,7 @@ void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) {
// A v4 boot image ramdisk will have to be merged with other vendor ramdisks, // A v4 boot image ramdisk will have to be merged with other vendor ramdisks,
// and they have to use the exact same compression method. v4 GKIs are required to // and they have to use the exact same compression method. v4 GKIs are required to
// use lz4 (legacy), so hardcode the format here. // use lz4 (legacy), so hardcode the format here.
fprintf(stderr, "RAMDISK_FMT: [%s] -> [%s]\n", fmt2name[r_fmt], fmt2name[FileFormat::LZ4_LEGACY]); fprintf(stderr, "RAMDISK_FMT: [%s] -> [%s]\n", fmt2name(r_fmt), fmt2name(FileFormat::LZ4_LEGACY));
r_fmt = FileFormat::LZ4_LEGACY; r_fmt = FileFormat::LZ4_LEGACY;
} }
if (!skip_comp && !COMPRESSED_ANY(check_fmt(m.buf(), m.sz())) && COMPRESSED(r_fmt)) { if (!skip_comp && !COMPRESSED_ANY(check_fmt(m.buf(), m.sz())) && COMPRESSED(r_fmt)) {
@@ -987,7 +1019,7 @@ void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) {
// Sign the image after we finish patching the boot image // Sign the image after we finish patching the boot image
if (boot.flags[AVB1_SIGNED_FLAG]) { if (boot.flags[AVB1_SIGNED_FLAG]) {
byte_view payload(out.buf() + off.header, off.total - off.header); byte_view payload(out.buf() + off.header, off.total - off.header);
auto sig = rust::sign_boot_image(payload, "/boot", nullptr, nullptr); auto sig = sign_payload(payload);
if (!sig.empty()) { if (!sig.empty()) {
lseek(fd, off.total, SEEK_SET); lseek(fd, off.total, SEEK_SET);
xwrite(fd, sig.data(), sig.size()); xwrite(fd, sig.data(), sig.size());
@@ -997,33 +1029,15 @@ void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) {
close(fd); close(fd);
} }
int verify(rust::Utf8CStr image, const char *cert) { void cleanup() {
const boot_img boot(image.data()); unlink(HEADER_FILE);
if (cert == nullptr) { unlink(KERNEL_FILE);
// Boot image parsing already checks if the image is signed unlink(RAMDISK_FILE);
return boot.flags[AVB1_SIGNED_FLAG] ? 0 : 1; unlink(SECOND_FILE);
} else { unlink(KER_DTB_FILE);
// Provide a custom certificate and re-verify unlink(EXTRA_FILE);
return boot.verify(cert) ? 0 : 1; unlink(RECV_DTBO_FILE);
} unlink(DTB_FILE);
} unlink(BOOTCONFIG_FILE);
rm_rf(VND_RAMDISK_DIR);
int sign(rust::Utf8CStr image, rust::Utf8CStr name, const char *cert, const char *key) {
const boot_img boot(image.data());
auto sig = rust::sign_boot_image(boot.payload, name.data(), cert, key);
if (sig.empty())
return 1;
auto eof = boot.tail.buf() - boot.map.buf();
int fd = xopen(image.data(), O_WRONLY | O_CLOEXEC);
if (lseek(fd, eof, SEEK_SET) != eof || xwrite(fd, sig.data(), sig.size()) != sig.size()) {
close(fd);
return 1;
}
if (auto off = lseek(fd, 0, SEEK_CUR); off < boot.map.sz()) {
// Wipe out rest of tail
write_zero(fd, boot.map.sz() - off);
}
close(fd);
return 0;
} }

View File

@@ -676,7 +676,12 @@ struct boot_img {
std::pair<const uint8_t *, dyn_img_hdr *> create_hdr(const uint8_t *addr, FileFormat type); std::pair<const uint8_t *, dyn_img_hdr *> create_hdr(const uint8_t *addr, FileFormat type);
// Rust FFI // Rust FFI
static std::unique_ptr<boot_img> create(rust::Utf8CStr name) { return std::make_unique<boot_img>(name.c_str()); }
rust::Slice<const uint8_t> get_payload() const { return payload; } rust::Slice<const uint8_t> get_payload() const { return payload; }
rust::Slice<const uint8_t> get_tail() const { return tail; } rust::Slice<const uint8_t> get_tail() const { return tail; }
bool verify(const char *cert = nullptr) const; bool is_signed() const { return flags[AVB1_SIGNED_FLAG]; }
uint64_t tail_off() const { return tail.buf() - map.buf(); }
// Implemented in Rust
bool verify() const noexcept;
}; };

View File

@@ -1,16 +1,17 @@
use crate::compress::{compress, decompress}; use crate::compress::{compress, decompress};
use crate::cpio::{cpio_commands, print_cpio_usage}; use crate::cpio::{cpio_commands, print_cpio_usage};
use crate::dtb::{DtbAction, dtb_commands, print_dtb_usage}; use crate::dtb::{DtbAction, dtb_commands, print_dtb_usage};
use crate::ffi::{FileFormat, cleanup, repack, sign, split_image_dtb, unpack, verify}; use crate::ffi::{BootImage, FileFormat, cleanup, repack, split_image_dtb, unpack};
use crate::patch::hexpatch; use crate::patch::hexpatch;
use crate::payload::extract_boot_from_payload; use crate::payload::extract_boot_from_payload;
use crate::sign::sha1_hash; use crate::sign::{sha1_hash, sign_boot_image};
use argh::FromArgs; use argh::FromArgs;
use base::{ use base::{
CmdArgs, EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, cmdline_logging, cstr, CmdArgs, EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, WriteExt,
libc::umask, log_err, cmdline_logging, cstr, libc, libc::umask, log_err,
}; };
use std::ffi::c_char; use std::ffi::c_char;
use std::io::{Seek, SeekFrom, Write};
use std::str::FromStr; use std::str::FromStr;
#[derive(FromArgs)] #[derive(FromArgs)]
@@ -159,7 +160,7 @@ fn print_usage(cmd: &str) {
eprintln!( eprintln!(
r#"MagiskBoot - Boot Image Modification Tool r#"MagiskBoot - Boot Image Modification Tool
Usage: {} <action> [args...] Usage: {0} <action> [args...]
Supported actions: Supported actions:
unpack [-n] [-h] <bootimg> unpack [-n] [-h] <bootimg>
@@ -259,6 +260,44 @@ Supported actions:
); );
} }
fn verify_cmd(image: &Utf8CStr, cert: Option<&Utf8CStr>) -> bool {
let image = BootImage::new(image);
match cert {
None => {
// Boot image parsing already checks if the image is signed
image.is_signed()
}
Some(_) => {
// Provide a custom certificate and re-verify
image.verify(cert).is_ok()
}
}
}
fn sign_cmd(
image: &Utf8CStr,
name: Option<&Utf8CStr>,
cert: Option<&Utf8CStr>,
key: Option<&Utf8CStr>,
) -> LoggedResult<()> {
let img = BootImage::new(image);
let name = name.unwrap_or(cstr!("/boot"));
let sig = sign_boot_image(img.payload(), name, cert, key)?;
let tail_off = img.tail_off();
drop(img);
let mut fd = image.open(libc::O_WRONLY | libc::O_CLOEXEC)?;
fd.seek(SeekFrom::Start(tail_off))?;
fd.write_all(&sig)?;
let current = fd.stream_position()?;
let eof = fd.seek(SeekFrom::End(0))?;
if eof > current {
// Zero out rest of the file
fd.seek(SeekFrom::Start(current))?;
fd.write_zeros((eof - current) as usize)?;
}
Ok(())
}
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn main(argc: i32, argv: *const *const c_char, _envp: *const *const c_char) -> i32 { pub extern "C" fn main(argc: i32, argv: *const *const c_char, _envp: *const *const c_char) -> i32 {
cmdline_logging(); cmdline_logging();
@@ -302,40 +341,24 @@ pub extern "C" fn main(argc: i32, argv: *const *const c_char, _envp: *const *con
no_compress, no_compress,
); );
} }
Action::Verify(Verify { Action::Verify(Verify { mut img, mut cert }) => {
ref mut img, return if verify_cmd(
ref mut cert, Utf8CStr::from_string(&mut img),
}) => { cert.as_mut().map(Utf8CStr::from_string),
return unsafe { ) {
verify( 0
Utf8CStr::from_string(img), } else {
cert.as_mut() 1
.map(|x| Utf8CStr::from_string(x).as_ptr())
.unwrap_or(std::ptr::null()),
)
}; };
} }
Action::Sign(Sign { Action::Sign(Sign { mut img, mut args }) => {
ref mut img, let mut iter = args.iter_mut();
ref mut args, sign_cmd(
}) => { Utf8CStr::from_string(&mut img),
let (pem, pk8) = match args.get_mut(1..=2) { iter.next().map(Utf8CStr::from_string),
Some([pem, pk8]) => ( iter.next().map(Utf8CStr::from_string),
Utf8CStr::from_string(pem).as_ptr(), iter.next().map(Utf8CStr::from_string),
Utf8CStr::from_string(pk8).as_ptr(), )?;
),
_ => (std::ptr::null(), std::ptr::null()),
};
return unsafe {
sign(
Utf8CStr::from_string(img),
args.first_mut()
.map(Utf8CStr::from_string)
.unwrap_or(cstr!("/boot")),
pem,
pk8,
)
};
} }
Action::Extract(Extract { Action::Extract(Extract {
ref payload, ref payload,

View File

@@ -1,72 +0,0 @@
#include "boot-rs.hpp"
#include "format.hpp"
Fmt2Name fmt2name;
#define CHECKED_MATCH(s) (len >= (sizeof(s) - 1) && BUFFER_MATCH(buf, s))
FileFormat check_fmt(const void *buf, size_t len) {
if (CHECKED_MATCH(CHROMEOS_MAGIC)) {
return FileFormat::CHROMEOS;
} else if (CHECKED_MATCH(BOOT_MAGIC)) {
return FileFormat::AOSP;
} else if (CHECKED_MATCH(VENDOR_BOOT_MAGIC)) {
return FileFormat::AOSP_VENDOR;
} else if (CHECKED_MATCH(GZIP1_MAGIC) || CHECKED_MATCH(GZIP2_MAGIC)) {
return FileFormat::GZIP;
} else if (CHECKED_MATCH(LZOP_MAGIC)) {
return FileFormat::LZOP;
} else if (CHECKED_MATCH(XZ_MAGIC)) {
return FileFormat::XZ;
} else if (len >= 13 && memcmp(buf, "\x5d\x00\x00", 3) == 0
&& (((char *)buf)[12] == '\xff' || ((char *)buf)[12] == '\x00')) {
return FileFormat::LZMA;
} else if (CHECKED_MATCH(BZIP_MAGIC)) {
return FileFormat::BZIP2;
} else if (CHECKED_MATCH(LZ41_MAGIC) || CHECKED_MATCH(LZ42_MAGIC)) {
return FileFormat::LZ4;
} else if (CHECKED_MATCH(LZ4_LEG_MAGIC)) {
return FileFormat::LZ4_LEGACY;
} else if (CHECKED_MATCH(MTK_MAGIC)) {
return FileFormat::MTK;
} else if (CHECKED_MATCH(DTB_MAGIC)) {
return FileFormat::DTB;
} else if (CHECKED_MATCH(DHTB_MAGIC)) {
return FileFormat::DHTB;
} else if (CHECKED_MATCH(TEGRABLOB_MAGIC)) {
return FileFormat::BLOB;
} else if (len >= 0x28 && memcmp(&((char *)buf)[0x24], ZIMAGE_MAGIC, 4) == 0) {
return FileFormat::ZIMAGE;
} else {
return FileFormat::UNKNOWN;
}
}
const char *Fmt2Name::operator[](FileFormat fmt) {
switch (fmt) {
case FileFormat::GZIP:
return "gzip";
case FileFormat::ZOPFLI:
return "zopfli";
case FileFormat::LZOP:
return "lzop";
case FileFormat::XZ:
return "xz";
case FileFormat::LZMA:
return "lzma";
case FileFormat::BZIP2:
return "bzip2";
case FileFormat::LZ4:
return "lz4";
case FileFormat::LZ4_LEGACY:
return "lz4_legacy";
case FileFormat::LZ4_LG:
return "lz4_lg";
case FileFormat::DTB:
return "dtb";
case FileFormat::ZIMAGE:
return "zimage";
default:
return "raw";
}
}

View File

@@ -1,7 +1,5 @@
#pragma once #pragma once
#include <string_view>
enum class FileFormat : ::std::uint8_t; enum class FileFormat : ::std::uint8_t;
#define COMPRESSED(fmt) ((+fmt) >= +FileFormat::GZIP && (+fmt) < +FileFormat::LZOP) #define COMPRESSED(fmt) ((+fmt) >= +FileFormat::GZIP && (+fmt) < +FileFormat::LZOP)
@@ -9,6 +7,7 @@ enum class FileFormat : ::std::uint8_t;
#define BUFFER_MATCH(buf, s) (memcmp(buf, s, sizeof(s) - 1) == 0) #define BUFFER_MATCH(buf, s) (memcmp(buf, s, sizeof(s) - 1) == 0)
#define BUFFER_CONTAIN(buf, sz, s) (memmem(buf, sz, s, sizeof(s) - 1) != nullptr) #define BUFFER_CONTAIN(buf, sz, s) (memmem(buf, sz, s, sizeof(s) - 1) != nullptr)
#define CHECKED_MATCH(s) (len >= (sizeof(s) - 1) && BUFFER_MATCH(buf, s))
#define BOOT_MAGIC "ANDROID!" #define BOOT_MAGIC "ANDROID!"
#define VENDOR_BOOT_MAGIC "VNDRBOOT" #define VENDOR_BOOT_MAGIC "VNDRBOOT"
@@ -41,20 +40,8 @@ enum class FileFormat : ::std::uint8_t;
#define AVB_MAGIC "AVB0" #define AVB_MAGIC "AVB0"
#define ZIMAGE_MAGIC "\x18\x28\x6f\x01" #define ZIMAGE_MAGIC "\x18\x28\x6f\x01"
class Fmt2Name {
public:
const char *operator[](FileFormat fmt);
};
class Name2Fmt {
public:
FileFormat operator[](std::string_view name);
};
FileFormat check_fmt(const void *buf, size_t len); FileFormat check_fmt(const void *buf, size_t len);
static inline FileFormat check_fmt(rust::Slice<const uint8_t> bytes) { static inline FileFormat check_fmt(rust::Slice<const uint8_t> bytes) {
return check_fmt(bytes.data(), bytes.size()); return check_fmt(bytes.data(), bytes.size());
} }
extern Fmt2Name fmt2name;

View File

@@ -1,4 +1,5 @@
use crate::ffi::FileFormat; use crate::ffi::FileFormat;
use base::{Utf8CStr, cstr, libc};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::str::FromStr; use std::str::FromStr;
@@ -22,23 +23,33 @@ impl FromStr for FileFormat {
impl Display for FileFormat { impl Display for FileFormat {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(self.to_cstr())
}
}
impl FileFormat {
fn to_cstr(&self) -> &'static Utf8CStr {
match *self { match *self {
Self::GZIP => write!(f, "gzip"), Self::GZIP => cstr!("gzip"),
Self::ZOPFLI => write!(f, "zopfli"), Self::ZOPFLI => cstr!("zopfli"),
Self::LZOP => write!(f, "lzop"), Self::LZOP => cstr!("lzop"),
Self::XZ => write!(f, "xz"), Self::XZ => cstr!("xz"),
Self::LZMA => write!(f, "lzma"), Self::LZMA => cstr!("lzma"),
Self::BZIP2 => write!(f, "bzip2"), Self::BZIP2 => cstr!("bzip2"),
Self::LZ4 => write!(f, "lz4"), Self::LZ4 => cstr!("lz4"),
Self::LZ4_LEGACY => write!(f, "lz4_legacy"), Self::LZ4_LEGACY => cstr!("lz4_legacy"),
Self::LZ4_LG => write!(f, "lz4_lg"), Self::LZ4_LG => cstr!("lz4_lg"),
Self::DTB => write!(f, "dtb"), Self::DTB => cstr!("dtb"),
Self::ZIMAGE => write!(f, "zimage"), Self::ZIMAGE => cstr!("zimage"),
_ => write!(f, "raw"), _ => cstr!("raw"),
} }
} }
} }
pub fn fmt2name(fmt: FileFormat) -> *const libc::c_char {
fmt.to_cstr().as_ptr()
}
impl FileFormat { impl FileFormat {
pub fn ext(&self) -> &'static str { pub fn ext(&self) -> &'static str {
match *self { match *self {

View File

@@ -5,7 +5,8 @@
pub use base; pub use base;
use compress::{compress_bytes, decompress_bytes}; use compress::{compress_bytes, decompress_bytes};
use sign::{SHA, get_sha, sha256_hash, sign_boot_image, verify_boot_image}; use format::fmt2name;
use sign::{SHA, get_sha, sha256_hash, sign_payload_for_cxx};
use std::env; use std::env;
mod compress; mod compress;
@@ -59,25 +60,10 @@ pub mod ffi {
include!("format.hpp"); include!("format.hpp");
fn check_fmt(buf: &[u8]) -> FileFormat; fn check_fmt(buf: &[u8]) -> FileFormat;
include!("bootimg.hpp");
#[cxx_name = "boot_img"]
type BootImage;
#[cxx_name = "get_payload"]
fn payload(self: &BootImage) -> &[u8];
#[cxx_name = "get_tail"]
fn tail(self: &BootImage) -> &[u8];
include!("magiskboot.hpp"); include!("magiskboot.hpp");
fn cleanup(); fn cleanup();
fn unpack(image: Utf8CStrRef, skip_decomp: bool, hdr: bool) -> i32; fn unpack(image: Utf8CStrRef, skip_decomp: bool, hdr: bool) -> i32;
fn repack(src_img: Utf8CStrRef, out_img: Utf8CStrRef, skip_comp: bool); fn repack(src_img: Utf8CStrRef, out_img: Utf8CStrRef, skip_comp: bool);
unsafe fn verify(image: Utf8CStrRef, cert: *const c_char) -> i32;
unsafe fn sign(
image: Utf8CStrRef,
name: Utf8CStrRef,
cert: *const c_char,
key: *const c_char,
) -> i32;
fn split_image_dtb(filename: Utf8CStrRef, skip_decomp: bool) -> i32; fn split_image_dtb(filename: Utf8CStrRef, skip_decomp: bool) -> i32;
} }
@@ -91,18 +77,32 @@ pub mod ffi {
fn compress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: i32); fn compress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: i32);
fn decompress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: i32); fn decompress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: i32);
fn fmt2name(fmt: FileFormat) -> *const c_char;
#[cxx_name = "sign_payload"]
fn sign_payload_for_cxx(payload: &[u8]) -> Vec<u8>;
} }
#[namespace = "rust"] // BootImage FFI
#[allow(unused_unsafe)] unsafe extern "C++" {
include!("bootimg.hpp");
#[cxx_name = "boot_img"]
type BootImage;
#[cxx_name = "get_payload"]
fn payload(self: &BootImage) -> &[u8];
#[cxx_name = "get_tail"]
fn tail(self: &BootImage) -> &[u8];
fn is_signed(self: &BootImage) -> bool;
fn tail_off(self: &BootImage) -> u64;
#[Self = BootImage]
#[cxx_name = "create"]
fn new(img: Utf8CStrRef) -> UniquePtr<BootImage>;
}
extern "Rust" { extern "Rust" {
unsafe fn sign_boot_image( #[cxx_name = "verify"]
payload: &[u8], fn verify_for_cxx(self: &BootImage) -> bool;
name: *const c_char,
cert: *const c_char,
key: *const c_char,
) -> Vec<u8>;
unsafe fn verify_boot_image(img: &BootImage, cert: *const c_char) -> bool;
} }
} }

View File

@@ -14,19 +14,5 @@
int unpack(rust::Utf8CStr image, bool skip_decomp = false, bool hdr = false); int unpack(rust::Utf8CStr image, bool skip_decomp = false, bool hdr = false);
void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp = false); void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp = false);
int verify(rust::Utf8CStr image, const char *cert);
int sign(rust::Utf8CStr image, rust::Utf8CStr name, const char *cert, const char *key);
int split_image_dtb(rust::Utf8CStr filename, bool skip_decomp = false); int split_image_dtb(rust::Utf8CStr filename, bool skip_decomp = false);
void cleanup();
inline void cleanup() {
unlink(HEADER_FILE);
unlink(KERNEL_FILE);
unlink(RAMDISK_FILE);
unlink(SECOND_FILE);
unlink(KER_DTB_FILE);
unlink(EXTRA_FILE);
unlink(RECV_DTBO_FILE);
unlink(DTB_FILE);
unlink(BOOTCONFIG_FILE);
rm_rf(VND_RAMDISK_DIR);
}

View File

@@ -25,8 +25,7 @@ use x509_cert::der::Any;
use x509_cert::der::asn1::{OctetString, PrintableString}; use x509_cert::der::asn1::{OctetString, PrintableString};
use x509_cert::spki::AlgorithmIdentifier; use x509_cert::spki::AlgorithmIdentifier;
use base::libc::c_char; use base::{LoggedError, LoggedResult, MappedFile, ResultExt, Utf8CStr, cstr, log_err};
use base::{LoggedResult, MappedFile, ResultExt, StrErr, Utf8CStr, log_err};
use crate::ffi::BootImage; use crate::ffi::BootImage;
@@ -265,23 +264,27 @@ impl BootSignature {
} }
} }
pub fn verify_boot_image(img: &BootImage, cert: *const c_char) -> bool { impl BootImage {
let res: LoggedResult<()> = try { pub fn verify(&self, cert: Option<&Utf8CStr>) -> LoggedResult<()> {
let tail = img.tail(); let tail = self.tail();
if tail.starts_with(b"AVB0") {
return Err(LoggedError::default());
}
// Don't use BootSignature::from_der because tail might have trailing zeros // Don't use BootSignature::from_der because tail might have trailing zeros
let mut reader = SliceReader::new(tail)?; let mut reader = SliceReader::new(tail)?;
let mut sig = BootSignature::decode(&mut reader)?; let mut sig = BootSignature::decode(&mut reader)?;
match unsafe { Utf8CStr::from_ptr(cert) } { if let Some(s) = cert {
Ok(s) => {
let pem = MappedFile::open(s)?; let pem = MappedFile::open(s)?;
sig.certificate = Certificate::from_pem(pem)?; sig.certificate = Certificate::from_pem(pem)?;
};
sig.verify(self.payload()).log()
}
pub fn verify_for_cxx(&self) -> bool {
self.verify(None).is_ok()
} }
Err(StrErr::NullPointerError) => {}
Err(e) => Err(e)?,
};
sig.verify(img.payload())?;
};
res.is_ok()
} }
enum Bytes { enum Bytes {
@@ -303,22 +306,17 @@ const VERITY_PK8: &[u8] = include_bytes!("../../../tools/keys/verity.pk8");
pub fn sign_boot_image( pub fn sign_boot_image(
payload: &[u8], payload: &[u8],
name: *const c_char, name: &Utf8CStr,
cert: *const c_char, cert: Option<&Utf8CStr>,
key: *const c_char, key: Option<&Utf8CStr>,
) -> Vec<u8> { ) -> LoggedResult<Vec<u8>> {
let res: LoggedResult<Vec<u8>> = try { let cert = match cert {
// Process arguments Some(s) => Bytes::Mapped(MappedFile::open(s)?),
let name = unsafe { Utf8CStr::from_ptr(name) }?; None => Bytes::Slice(VERITY_PEM),
let cert = match unsafe { Utf8CStr::from_ptr(cert) } {
Ok(s) => Bytes::Mapped(MappedFile::open(s)?),
Err(StrErr::NullPointerError) => Bytes::Slice(VERITY_PEM),
Err(e) => Err(e)?,
}; };
let key = match unsafe { Utf8CStr::from_ptr(key) } { let key = match key {
Ok(s) => Bytes::Mapped(MappedFile::open(s)?), Some(s) => Bytes::Mapped(MappedFile::open(s)?),
Err(StrErr::NullPointerError) => Bytes::Slice(VERITY_PK8), None => Bytes::Slice(VERITY_PK8),
Err(e) => Err(e)?,
}; };
// Parse cert and private key // Parse cert and private key
@@ -343,7 +341,9 @@ pub fn sign_boot_image(
authenticated_attributes: attr, authenticated_attributes: attr,
signature: OctetString::new(sig)?, signature: OctetString::new(sig)?,
}; };
sig.to_der()? sig.to_der().log()
}; }
res.unwrap_or_default()
pub fn sign_payload_for_cxx(payload: &[u8]) -> Vec<u8> {
sign_boot_image(payload, cstr!("/boot"), None, None).unwrap_or_default()
} }