mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-10-27 16:41:38 +00:00
Simplify magiskboot FFI
This commit is contained in:
@@ -87,7 +87,6 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
boot/bootimg.cpp \
|
||||
boot/format.cpp \
|
||||
boot/boot-rs.cpp
|
||||
|
||||
LOCAL_LDFLAGS := -static
|
||||
|
||||
@@ -48,6 +48,43 @@ static bool check_env(const char *name) {
|
||||
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 {
|
||||
uint32_t ver = header_version();
|
||||
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, "%-*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 (hdr->vendor_ramdisk_table_size()) {
|
||||
@@ -493,7 +530,7 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
|
||||
fprintf(stderr,
|
||||
"%-*s name=[%s] type=[%s] size=[%u] fmt=[%s]\n", PADDING, "VND_RAMDISK",
|
||||
it.ramdisk_name, vendor_ramdisk_type(it.ramdisk_type),
|
||||
it.ramdisk_size, fmt2name[fmt]);
|
||||
it.ramdisk_size, fmt2name(fmt));
|
||||
}
|
||||
} else {
|
||||
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);
|
||||
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()) {
|
||||
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()) {
|
||||
@@ -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)) {
|
||||
fprintf(stderr, "LG_BUMP_IMAGE\n");
|
||||
flags[LG_BUMP_FLAG] = true;
|
||||
} else if (!(tail.sz() >= 4 && BUFFER_MATCH(tail.buf(), AVB_MAGIC)) && verify()) {
|
||||
// Check if the image is avb 1.0 signed
|
||||
} else if (verify()) {
|
||||
fprintf(stderr, "AVB1_SIGNED\n");
|
||||
flags[AVB1_SIGNED_FLAG] = true;
|
||||
}
|
||||
@@ -532,13 +568,13 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
|
||||
// Find AVB footer
|
||||
const void *footer = tail.buf() + tail.sz() - sizeof(AvbFooter);
|
||||
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
|
||||
const void *meta = base_addr + __builtin_bswap64(avb_footer->vbmeta_offset);
|
||||
if (BUFFER_MATCH(meta, AVB_MAGIC)) {
|
||||
fprintf(stderr, "VBMETA\n");
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
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,
|
||||
// and they have to use the exact same compression method. v4 GKIs are required to
|
||||
// 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;
|
||||
}
|
||||
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
|
||||
if (boot.flags[AVB1_SIGNED_FLAG]) {
|
||||
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()) {
|
||||
lseek(fd, off.total, SEEK_SET);
|
||||
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);
|
||||
}
|
||||
|
||||
int verify(rust::Utf8CStr image, const char *cert) {
|
||||
const boot_img boot(image.data());
|
||||
if (cert == nullptr) {
|
||||
// Boot image parsing already checks if the image is signed
|
||||
return boot.flags[AVB1_SIGNED_FLAG] ? 0 : 1;
|
||||
} else {
|
||||
// Provide a custom certificate and re-verify
|
||||
return boot.verify(cert) ? 0 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -676,7 +676,12 @@ struct boot_img {
|
||||
std::pair<const uint8_t *, dyn_img_hdr *> create_hdr(const uint8_t *addr, FileFormat type);
|
||||
|
||||
// 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_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;
|
||||
};
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
use crate::compress::{compress, decompress};
|
||||
use crate::cpio::{cpio_commands, print_cpio_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::payload::extract_boot_from_payload;
|
||||
use crate::sign::sha1_hash;
|
||||
use crate::sign::{sha1_hash, sign_boot_image};
|
||||
use argh::FromArgs;
|
||||
use base::{
|
||||
CmdArgs, EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, cmdline_logging, cstr,
|
||||
libc::umask, log_err,
|
||||
CmdArgs, EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, WriteExt,
|
||||
cmdline_logging, cstr, libc, libc::umask, log_err,
|
||||
};
|
||||
use std::ffi::c_char;
|
||||
use std::io::{Seek, SeekFrom, Write};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(FromArgs)]
|
||||
@@ -159,7 +160,7 @@ fn print_usage(cmd: &str) {
|
||||
eprintln!(
|
||||
r#"MagiskBoot - Boot Image Modification Tool
|
||||
|
||||
Usage: {} <action> [args...]
|
||||
Usage: {0} <action> [args...]
|
||||
|
||||
Supported actions:
|
||||
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)]
|
||||
pub extern "C" fn main(argc: i32, argv: *const *const c_char, _envp: *const *const c_char) -> i32 {
|
||||
cmdline_logging();
|
||||
@@ -302,40 +341,24 @@ pub extern "C" fn main(argc: i32, argv: *const *const c_char, _envp: *const *con
|
||||
no_compress,
|
||||
);
|
||||
}
|
||||
Action::Verify(Verify {
|
||||
ref mut img,
|
||||
ref mut cert,
|
||||
}) => {
|
||||
return unsafe {
|
||||
verify(
|
||||
Utf8CStr::from_string(img),
|
||||
cert.as_mut()
|
||||
.map(|x| Utf8CStr::from_string(x).as_ptr())
|
||||
.unwrap_or(std::ptr::null()),
|
||||
)
|
||||
Action::Verify(Verify { mut img, mut cert }) => {
|
||||
return if verify_cmd(
|
||||
Utf8CStr::from_string(&mut img),
|
||||
cert.as_mut().map(Utf8CStr::from_string),
|
||||
) {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
};
|
||||
}
|
||||
Action::Sign(Sign {
|
||||
ref mut img,
|
||||
ref mut args,
|
||||
}) => {
|
||||
let (pem, pk8) = match args.get_mut(1..=2) {
|
||||
Some([pem, pk8]) => (
|
||||
Utf8CStr::from_string(pem).as_ptr(),
|
||||
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::Sign(Sign { mut img, mut args }) => {
|
||||
let mut iter = args.iter_mut();
|
||||
sign_cmd(
|
||||
Utf8CStr::from_string(&mut img),
|
||||
iter.next().map(Utf8CStr::from_string),
|
||||
iter.next().map(Utf8CStr::from_string),
|
||||
iter.next().map(Utf8CStr::from_string),
|
||||
)?;
|
||||
}
|
||||
Action::Extract(Extract {
|
||||
ref payload,
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
enum class FileFormat : ::std::uint8_t;
|
||||
|
||||
#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_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 VENDOR_BOOT_MAGIC "VNDRBOOT"
|
||||
@@ -41,20 +40,8 @@ enum class FileFormat : ::std::uint8_t;
|
||||
#define AVB_MAGIC "AVB0"
|
||||
#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);
|
||||
|
||||
static inline FileFormat check_fmt(rust::Slice<const uint8_t> bytes) {
|
||||
return check_fmt(bytes.data(), bytes.size());
|
||||
}
|
||||
|
||||
extern Fmt2Name fmt2name;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::ffi::FileFormat;
|
||||
use base::{Utf8CStr, cstr, libc};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
|
||||
@@ -22,23 +23,33 @@ impl FromStr for FileFormat {
|
||||
|
||||
impl Display for FileFormat {
|
||||
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 {
|
||||
Self::GZIP => write!(f, "gzip"),
|
||||
Self::ZOPFLI => write!(f, "zopfli"),
|
||||
Self::LZOP => write!(f, "lzop"),
|
||||
Self::XZ => write!(f, "xz"),
|
||||
Self::LZMA => write!(f, "lzma"),
|
||||
Self::BZIP2 => write!(f, "bzip2"),
|
||||
Self::LZ4 => write!(f, "lz4"),
|
||||
Self::LZ4_LEGACY => write!(f, "lz4_legacy"),
|
||||
Self::LZ4_LG => write!(f, "lz4_lg"),
|
||||
Self::DTB => write!(f, "dtb"),
|
||||
Self::ZIMAGE => write!(f, "zimage"),
|
||||
_ => write!(f, "raw"),
|
||||
Self::GZIP => cstr!("gzip"),
|
||||
Self::ZOPFLI => cstr!("zopfli"),
|
||||
Self::LZOP => cstr!("lzop"),
|
||||
Self::XZ => cstr!("xz"),
|
||||
Self::LZMA => cstr!("lzma"),
|
||||
Self::BZIP2 => cstr!("bzip2"),
|
||||
Self::LZ4 => cstr!("lz4"),
|
||||
Self::LZ4_LEGACY => cstr!("lz4_legacy"),
|
||||
Self::LZ4_LG => cstr!("lz4_lg"),
|
||||
Self::DTB => cstr!("dtb"),
|
||||
Self::ZIMAGE => cstr!("zimage"),
|
||||
_ => cstr!("raw"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmt2name(fmt: FileFormat) -> *const libc::c_char {
|
||||
fmt.to_cstr().as_ptr()
|
||||
}
|
||||
|
||||
impl FileFormat {
|
||||
pub fn ext(&self) -> &'static str {
|
||||
match *self {
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
pub use base;
|
||||
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;
|
||||
|
||||
mod compress;
|
||||
@@ -59,25 +60,10 @@ pub mod ffi {
|
||||
include!("format.hpp");
|
||||
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");
|
||||
fn cleanup();
|
||||
fn unpack(image: Utf8CStrRef, skip_decomp: bool, hdr: bool) -> i32;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -91,18 +77,32 @@ pub mod ffi {
|
||||
|
||||
fn compress_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"]
|
||||
#[allow(unused_unsafe)]
|
||||
// BootImage FFI
|
||||
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" {
|
||||
unsafe fn sign_boot_image(
|
||||
payload: &[u8],
|
||||
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;
|
||||
#[cxx_name = "verify"]
|
||||
fn verify_for_cxx(self: &BootImage) -> bool;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,19 +14,5 @@
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
void cleanup();
|
||||
|
||||
@@ -25,8 +25,7 @@ use x509_cert::der::Any;
|
||||
use x509_cert::der::asn1::{OctetString, PrintableString};
|
||||
use x509_cert::spki::AlgorithmIdentifier;
|
||||
|
||||
use base::libc::c_char;
|
||||
use base::{LoggedResult, MappedFile, ResultExt, StrErr, Utf8CStr, log_err};
|
||||
use base::{LoggedError, LoggedResult, MappedFile, ResultExt, Utf8CStr, cstr, log_err};
|
||||
|
||||
use crate::ffi::BootImage;
|
||||
|
||||
@@ -265,23 +264,27 @@ impl BootSignature {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify_boot_image(img: &BootImage, cert: *const c_char) -> bool {
|
||||
let res: LoggedResult<()> = try {
|
||||
let tail = img.tail();
|
||||
impl BootImage {
|
||||
pub fn verify(&self, cert: Option<&Utf8CStr>) -> LoggedResult<()> {
|
||||
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
|
||||
let mut reader = SliceReader::new(tail)?;
|
||||
let mut sig = BootSignature::decode(&mut reader)?;
|
||||
match unsafe { Utf8CStr::from_ptr(cert) } {
|
||||
Ok(s) => {
|
||||
let pem = MappedFile::open(s)?;
|
||||
sig.certificate = Certificate::from_pem(pem)?;
|
||||
}
|
||||
Err(StrErr::NullPointerError) => {}
|
||||
Err(e) => Err(e)?,
|
||||
if let Some(s) = cert {
|
||||
let pem = MappedFile::open(s)?;
|
||||
sig.certificate = Certificate::from_pem(pem)?;
|
||||
};
|
||||
sig.verify(img.payload())?;
|
||||
};
|
||||
res.is_ok()
|
||||
|
||||
sig.verify(self.payload()).log()
|
||||
}
|
||||
|
||||
pub fn verify_for_cxx(&self) -> bool {
|
||||
self.verify(None).is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
enum Bytes {
|
||||
@@ -303,47 +306,44 @@ const VERITY_PK8: &[u8] = include_bytes!("../../../tools/keys/verity.pk8");
|
||||
|
||||
pub fn sign_boot_image(
|
||||
payload: &[u8],
|
||||
name: *const c_char,
|
||||
cert: *const c_char,
|
||||
key: *const c_char,
|
||||
) -> Vec<u8> {
|
||||
let res: LoggedResult<Vec<u8>> = try {
|
||||
// Process arguments
|
||||
let name = unsafe { Utf8CStr::from_ptr(name) }?;
|
||||
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) } {
|
||||
Ok(s) => Bytes::Mapped(MappedFile::open(s)?),
|
||||
Err(StrErr::NullPointerError) => Bytes::Slice(VERITY_PK8),
|
||||
Err(e) => Err(e)?,
|
||||
};
|
||||
|
||||
// Parse cert and private key
|
||||
let cert = Certificate::from_pem(cert)?;
|
||||
let mut signer = Signer::from_private_key(key.as_ref())?;
|
||||
|
||||
// Sign image
|
||||
let attr = AuthenticatedAttributes {
|
||||
target: PrintableString::new(name.as_bytes())?,
|
||||
length: payload.len() as u64,
|
||||
};
|
||||
signer.update(payload);
|
||||
signer.update(attr.to_der()?.as_slice());
|
||||
let sig = signer.sign()?;
|
||||
|
||||
// Create BootSignature DER
|
||||
let alg_id = cert.signature_algorithm().clone();
|
||||
let sig = BootSignature {
|
||||
format_version: 1,
|
||||
certificate: cert,
|
||||
algorithm_identifier: alg_id,
|
||||
authenticated_attributes: attr,
|
||||
signature: OctetString::new(sig)?,
|
||||
};
|
||||
sig.to_der()?
|
||||
name: &Utf8CStr,
|
||||
cert: Option<&Utf8CStr>,
|
||||
key: Option<&Utf8CStr>,
|
||||
) -> LoggedResult<Vec<u8>> {
|
||||
let cert = match cert {
|
||||
Some(s) => Bytes::Mapped(MappedFile::open(s)?),
|
||||
None => Bytes::Slice(VERITY_PEM),
|
||||
};
|
||||
res.unwrap_or_default()
|
||||
let key = match key {
|
||||
Some(s) => Bytes::Mapped(MappedFile::open(s)?),
|
||||
None => Bytes::Slice(VERITY_PK8),
|
||||
};
|
||||
|
||||
// Parse cert and private key
|
||||
let cert = Certificate::from_pem(cert)?;
|
||||
let mut signer = Signer::from_private_key(key.as_ref())?;
|
||||
|
||||
// Sign image
|
||||
let attr = AuthenticatedAttributes {
|
||||
target: PrintableString::new(name.as_bytes())?,
|
||||
length: payload.len() as u64,
|
||||
};
|
||||
signer.update(payload);
|
||||
signer.update(attr.to_der()?.as_slice());
|
||||
let sig = signer.sign()?;
|
||||
|
||||
// Create BootSignature DER
|
||||
let alg_id = cert.signature_algorithm().clone();
|
||||
let sig = BootSignature {
|
||||
format_version: 1,
|
||||
certificate: cert,
|
||||
algorithm_identifier: alg_id,
|
||||
authenticated_attributes: attr,
|
||||
signature: OctetString::new(sig)?,
|
||||
};
|
||||
sig.to_der().log()
|
||||
}
|
||||
|
||||
pub fn sign_payload_for_cxx(payload: &[u8]) -> Vec<u8> {
|
||||
sign_boot_image(payload, cstr!("/boot"), None, None).unwrap_or_default()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user