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 := \
boot/bootimg.cpp \
boot/format.cpp \
boot/boot-rs.cpp
LOCAL_LDFLAGS := -static

View File

@@ -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);
}

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);
// 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;
};

View File

@@ -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,

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
#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;

View File

@@ -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 {

View File

@@ -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;
}
}

View File

@@ -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();

View File

@@ -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()
}