Compare commits

...

9 Commits

Author SHA1 Message Date
Andrew Gunnerson f2b5d9646e Version 3.26.1
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2026-02-23 22:27:43 -05:00
Andrew Gunnerson 4991c5192e CHANGELOG.md: Add entry for PR #573
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2026-02-23 22:14:38 -05:00
Andrew Gunnerson 8ef9f89cdd Bump Magisk version upper bound to 30800
There are no breaking changes.

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2026-02-23 22:12:09 -05:00
Andrew Gunnerson 43cd7eac78 Version 3.26.0
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2026-02-21 16:44:08 -05:00
Andrew Gunnerson faf5a908ec CHANGELOG.md: Add entry for PR #571
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2026-02-21 16:39:19 -05:00
Andrew Gunnerson 97cad8f508 cli/avb: Warn when AVB header is signed with AOSP test keys
This will log a warning message, but will not return a fatal error.

Issue: #569

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2026-02-21 16:33:56 -05:00
Andrew Gunnerson dd73dcbd56 CHANGELOG.md: Add entry for PR #570
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2026-02-21 16:32:15 -05:00
Andrew Gunnerson 6f8de8b2c4 Add option to re-sign unmodified partitions during patching
This is useful when the OTA contains partition images that are signed
with the AOSP test keys.

Issue: #569

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2026-02-21 15:32:44 -05:00
Andrew Gunnerson 1be81d87b9 README.extra.md: Fix avbroot ota -> avbroot zip
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2026-02-20 23:14:17 -05:00
10 changed files with 231 additions and 32 deletions
+13
View File
@@ -7,6 +7,15 @@
to update the actual links at the bottom of the file.
-->
### Version 3.26.1
* Add support for Magisk 30700 ([PR #573])
### Version 3.26.0
* Add new `--re-sign` option to `avbroot ota patch` for re-signing partitions that would otherwise be unmodified ([Issue #569], [PR #570])
* Warn when a partition is signed with an AOSP test key in `avbroot ota verify` ([Issue #569], [PR #571])
### Version 3.25.0
* Remove outdated mentions of guarantees provided by cap-std in `--help` text, which was removed in version 3.20.0 ([PR #561])
@@ -464,6 +473,7 @@ Behind-the-scenes changes:
[Issue #536]: https://github.com/chenxiaolong/avbroot/issues/536
[Issue #537]: https://github.com/chenxiaolong/avbroot/issues/537
[Issue #554]: https://github.com/chenxiaolong/avbroot/issues/554
[Issue #569]: https://github.com/chenxiaolong/avbroot/issues/569
[PR #130]: https://github.com/chenxiaolong/avbroot/pull/130
[PR #132]: https://github.com/chenxiaolong/avbroot/pull/132
[PR #133]: https://github.com/chenxiaolong/avbroot/pull/133
@@ -686,3 +696,6 @@ Behind-the-scenes changes:
[PR #563]: https://github.com/chenxiaolong/avbroot/pull/563
[PR #564]: https://github.com/chenxiaolong/avbroot/pull/564
[PR #565]: https://github.com/chenxiaolong/avbroot/pull/565
[PR #570]: https://github.com/chenxiaolong/avbroot/pull/570
[PR #571]: https://github.com/chenxiaolong/avbroot/pull/571
[PR #573]: https://github.com/chenxiaolong/avbroot/pull/573
Generated
+4 -4
View File
@@ -104,7 +104,7 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "avbroot"
version = "3.25.0"
version = "3.26.1"
dependencies = [
"anyhow",
"assert_matches",
@@ -567,7 +567,7 @@ checksum = "ecb08c4819242b1ec89b3d0c6affa229005bef46ae4f7eed8b80768187c10087"
[[package]]
name = "e2e"
version = "3.25.0"
version = "3.26.1"
dependencies = [
"anyhow",
"avbroot",
@@ -663,7 +663,7 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "fuzz"
version = "3.25.0"
version = "3.26.1"
dependencies = [
"avbroot",
"honggfuzz",
@@ -2239,7 +2239,7 @@ dependencies = [
[[package]]
name = "xtask"
version = "3.25.0"
version = "3.26.1"
dependencies = [
"anyhow",
"clap",
+1 -1
View File
@@ -4,7 +4,7 @@ members = ["avbroot", "e2e", "fuzz", "xtask"]
resolver = "2"
[workspace.package]
version = "3.25.0"
version = "3.26.1"
license = "GPL-3.0-only"
edition = "2024"
repository = "https://github.com/chenxiaolong/avbroot"
+6 -6
View File
@@ -429,7 +429,7 @@ This set of commands is for working with raw OTA zip files. They are intentional
### Unpacking an OTA zip
```bash
avbroot ota unpack -i <input OTA>
avbroot zip unpack -i <input OTA>
```
This subcommand unpacks the OTA metadata to `ota.toml` and the OTA files to the `ota_files` directory.
@@ -437,7 +437,7 @@ This subcommand unpacks the OTA metadata to `ota.toml` and the OTA files to the
### Packing an OTA zip
```bash
avbroot ota pack -o <output OTA> -k <OTA private key>
avbroot zip pack -o <output OTA> -k <OTA private key>
```
This subcommand packs a new OTA zip from the `ota.toml` file and `ota_files` directory. Any files in the `ota_files` directory that don't have a corresponding entry in `ota.toml` are silently ignored.
@@ -447,19 +447,19 @@ When packing an OTA zip, the `metadata.property_files` field in `ota.toml` may p
### Repacking an OTA zip
```bash
avbroot ota repack -i <input OTA> -o <output OTA> -k <OTA private key>
avbroot zip repack -i <input OTA> -o <output OTA> -k <OTA private key>
```
This subcommand is logically equivalent to `avbroot ota unpack` followed by `avbroot ota pack`, except more efficient.
This subcommand is logically equivalent to `avbroot zip unpack` followed by `avbroot zip pack`, except more efficient.
**WARNING**: This is generally not a useful command. Resigning the OTA zip without also resigning the payload binary inside results in an invalid OTA.
### Showing OTA metadata
```bash
avbroot ota info -i <input OTA>
avbroot zip info -i <input OTA>
```
This subcommand shows all of the OTA metadata fields. If both the modern protobuf metadata and the legacy plain text metadata exist, the protobuf metadata takes precedence.
(All of the `ota` subcommands show this information. This specific subcommand just does so without performing any other operation.)
(All of the `zip` subcommands show this information. This specific subcommand just does so without performing any other operation.)
+4
View File
@@ -421,6 +421,10 @@ The only behavior this changes is where the partition is read from. When using `
This has no impact on what patches are applied. For example, when using Magisk, the root patch is applied to the boot partition, no matter if the partition came from the original `payload.bin` or from `--replace`.
### Re-signing partitions
avbroot will automatically re-sign any partitions in the OTA that it modifies. However, partitions that are otherwise unmodified can also be re-signed with `--re-sign <partition name>`. This is useful, for example, when the OTA contains partitions signed with the public AOSP test key.
### Booting signed GSIs
Android's [Dynamic System Updates (DSU)](https://developer.android.com/topic/dsu) feature uses a different root of trust than the regular system. Instead of using the bootloader's `avb_custom_key`, it obtains the trusted keys from the `first_stage_ramdisk/avb/*.avbpubkey` files inside the `init_boot` or `vendor_boot` ramdisk. These files are encoded in the same binary format as `avb_pkmd.bin`.
+53 -3
View File
@@ -29,6 +29,53 @@ use crate::{
util,
};
/// SHA-256 digests of the AOSP test keys encoded in the AVB binary format. This
/// excludes 8192-bit keys since RustCrypto doesn't support them anyway.
const PUBLIC_PUBKEYS: [[u8; 32]; 7] = [
// testkey_cert_pik.pem
[
0xce, 0x46, 0x4c, 0x0f, 0xef, 0xc0, 0x23, 0x76, 0x0a, 0xdc, 0x3f, 0xe0, 0x20, 0x7c, 0x28,
0x2d, 0x33, 0x7e, 0x1b, 0x91, 0xe3, 0xdf, 0x7a, 0xb4, 0xfe, 0xe4, 0x3f, 0xcd, 0x62, 0x0d,
0xb4, 0x6d,
],
// testkey_cert_prk.pem
[
0x86, 0xb0, 0x4a, 0x9d, 0x62, 0x80, 0x7f, 0xda, 0x80, 0xcb, 0xf4, 0x45, 0x11, 0x8e, 0x8a,
0x4a, 0x73, 0xaa, 0x59, 0x37, 0x28, 0x3b, 0x49, 0xe0, 0x18, 0xe4, 0x32, 0xdd, 0x8e, 0x3a,
0x76, 0xa1,
],
// testkey_cert_psk.pem
[
0x7e, 0xc0, 0x2e, 0xe1, 0xbe, 0x69, 0x63, 0x66, 0xf3, 0xfa, 0x91, 0x24, 0x0a, 0x8e, 0xc6,
0x81, 0x25, 0xc4, 0x14, 0x5d, 0x69, 0x8f, 0x59, 0x7a, 0xa2, 0xb3, 0x46, 0x4b, 0x59, 0xca,
0x7f, 0xc3,
],
// testkey_cert_puk.pem
[
0xc6, 0x08, 0xff, 0xa5, 0xe7, 0xee, 0x92, 0x98, 0x4f, 0x44, 0x86, 0xe0, 0x30, 0x88, 0x44,
0x0c, 0x36, 0x0c, 0xf3, 0x15, 0xec, 0x6a, 0x5f, 0x52, 0xae, 0xf7, 0x90, 0xcb, 0x30, 0xef,
0xce, 0xc9,
],
// testkey_rsa2048.pem
[
0x22, 0xde, 0x39, 0x94, 0x53, 0x21, 0x96, 0xf6, 0x1c, 0x03, 0x9e, 0x90, 0x26, 0x0d, 0x78,
0xa9, 0x3a, 0x4c, 0x57, 0x36, 0x2c, 0x7e, 0x78, 0x9b, 0xe9, 0x28, 0x03, 0x6e, 0x80, 0xb7,
0x7c, 0x8c,
],
// testkey_rsa2048_2.pem
[
0x2b, 0xed, 0x47, 0x45, 0x1b, 0xc6, 0x98, 0xe9, 0xe8, 0x2d, 0x92, 0xa6, 0x66, 0x8b, 0xd0,
0x3a, 0xb6, 0xcf, 0x8d, 0xd1, 0xa1, 0x44, 0x34, 0x1c, 0xb7, 0xf4, 0x26, 0xf2, 0x0b, 0x28,
0x79, 0xcf,
],
// testkey_rsa4096.pem
[
0x77, 0x28, 0xe3, 0x0f, 0x50, 0xbf, 0xa5, 0xce, 0xa1, 0x65, 0xf4, 0x73, 0x17, 0x5a, 0x08,
0x80, 0x3f, 0x6a, 0x83, 0x46, 0x64, 0x2b, 0x5a, 0xa1, 0x09, 0x13, 0xe9, 0xd9, 0xe6, 0xde,
0xfe, 0xf6,
],
];
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
struct AvbInfo {
header: Header,
@@ -500,6 +547,8 @@ pub fn verify_headers(
if let Some(k) = &public_key {
let prefix = format!("{name} has a signed vbmeta header");
let encoded = avb::encode_public_key(k)?;
let digest = Sha256::digest(&encoded);
match trust_method {
TrustMethod::Key(expected) => {
@@ -510,9 +559,6 @@ pub fn verify_headers(
}
}
TrustMethod::KeyDigest(expected_sha256) => {
let encoded = avb::encode_public_key(k)?;
let digest = Sha256::digest(&encoded);
if digest.as_slice() == expected_sha256 {
info!("{prefix}");
} else {
@@ -523,6 +569,10 @@ pub fn verify_headers(
warn!("{prefix}, but parent does not list a trusted key");
}
}
if PUBLIC_PUBKEYS.iter().any(|p| p == digest.as_slice()) {
warn!("{name} is signed with public AOSP test key");
}
} else {
info!("{name} has an unsigned vbmeta header");
}
+144 -14
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022-2025 Andrew Gunnerson
// SPDX-FileCopyrightText: 2022-2026 Andrew Gunnerson
// SPDX-License-Identifier: GPL-3.0-only
use std::{
@@ -23,7 +23,7 @@ use rawzip::{
use rayon::{iter::IntoParallelRefIterator, prelude::ParallelIterator};
use tempfile::{NamedTempFile, TempDir};
use topological_sort::TopologicalSort;
use tracing::{debug_span, error, info, warn};
use tracing::{Span, debug_span, error, info, warn};
use x509_cert::Certificate;
use crate::{
@@ -35,7 +35,6 @@ use crate::{
format::{
avb::{self, Descriptor, Header},
ota::{self, SigningWriter, ZipEntry, ZipMode},
padding,
payload::{self, CowVersion, PayloadHeader, PayloadWriter, VabcAlgo, VabcParams},
zip::{
self, ReaderAtWrapper, ZipArchiveReadAtExt, ZipEntriesSafeExt, ZipFileHeaderRecordExt,
@@ -53,7 +52,7 @@ use crate::{
},
stream::{
self, FromReader, HashingWriter, MutexFile, ReadAt, ReadSeek, SectionReader,
SectionReaderAt, ToWriter, UserPosFile, WriteAt, WriteSeek,
SectionReaderAt, UserPosFile, WriteAt, WriteSeek,
},
util,
};
@@ -155,19 +154,23 @@ enum InputFileState {
Modified,
}
#[derive(Clone)]
struct InputFile {
file: Arc<File>,
state: InputFileState,
}
/// Open all input files listed in `required_images`. If an image has a path
/// in `external_images`, that file is opened. Otherwise, the image is extracted
/// from the payload into a temporary file (that is unnamed if supported by the
/// operating system).
/// Open all input files listed in `required_images` and `re_sign_images`. If an
/// image has a path in `external_images`, that file is opened. Otherwise, the
/// image is extracted from the payload into a temporary file (that is unnamed
/// if supported by the operating system). Images that are destined to be
/// re-signed do not get copied to a temporary file until later when it can be
/// determined that it wasn't already modified and re-signed due to patching.
fn open_input_files(
payload: &(dyn ReadAt + Sync),
required_images: &HashMap<String, PartitionFlags>,
external_images: &HashMap<String, PathBuf>,
re_sign_images: &HashSet<String>,
header: &PayloadHeader,
cancel_signal: &AtomicBool,
) -> Result<HashMap<String, InputFile>> {
@@ -178,6 +181,7 @@ fn open_input_files(
let all_images = required_images
.keys()
.chain(external_images.keys())
.chain(re_sign_images.iter())
.collect::<HashSet<_>>();
for name in all_images {
@@ -325,6 +329,102 @@ fn patch_system_image<'a>(
Ok((target, ranges))
}
/// Re-sign the images listed in `re_sign_images` if they aren't already
/// modified by the patching process. If the original image is signed, then it
/// will be re-signed with `key_avb`. Otherwise, it is left untouched. If the
/// original image is external, then it will be copied to a temporary file.
fn re_sign_unmodified_images(
re_sign_images: &HashSet<String>,
input_files: &mut HashMap<String, InputFile>,
key_avb: &RsaSigningKey,
block_size: u64,
cancel_signal: &AtomicBool,
) -> Result<()> {
let parent_span = Span::current();
let input_files = Mutex::new(input_files);
re_sign_images
.par_iter()
.try_for_each(|name| -> Result<()> {
let _span = debug_span!(parent: &parent_span, "image", name).entered();
let mut input_file = input_files.lock().unwrap()[name].clone();
if input_file.state == InputFileState::Modified {
// Already re-signed by some other patching operation.
return Ok(());
}
let (mut header, footer, image_size) = avb::load_image(&mut input_file.file)
.with_context(|| format!("Failed to load AVB metadata: {name}"))?;
let orig_header = header.clone();
if !header.public_key.is_empty() {
header
.set_algo_for_key(key_avb)
.with_context(|| format!("Failed to set signature algorithm: {name}"))?;
header
.sign(key_avb)
.with_context(|| format!("Failed to sign AVB metadata: {name}"))?;
}
if header == orig_header {
return Ok(());
}
if input_file.state == InputFileState::External {
info!("Copying external image for re-signing: {name}");
// Need to make a copy so that the user's original file doesn't
// get modified.
let mut temp_file = tempfile::tempfile()
.with_context(|| format!("Failed to create temp file for: {name}"))?;
input_file
.file
.rewind()
.with_context(|| format!("Failed to rewind file: {name}"))?;
stream::copy(&mut input_file.file.clone(), &mut temp_file, cancel_signal)
.with_context(|| format!("Failed to copy file: {name}"))?;
input_file.file = Arc::new(temp_file);
}
info!("Re-signing image: {name}");
let original_image_size = footer
.as_ref()
.map(|f| f.original_image_size)
.unwrap_or_default();
input_file
.file
.rewind()
.with_context(|| format!("Failed to rewind file: {name}"))?;
input_file
.file
.set_len(original_image_size)
.with_context(|| format!("Failed to truncate file: {name}"))?;
if let Some(mut footer) = footer {
avb::write_appended_image(
&mut input_file.file,
&header,
&mut footer,
Some(image_size),
)
} else {
avb::write_root_image(&mut input_file.file, &header, block_size)
}
.with_context(|| format!("Failed to write AVB metadata: {name}"))?;
input_file.state = InputFileState::Modified;
*input_files.lock().unwrap().get_mut(name).unwrap() = input_file;
Ok(())
})
}
/// Load the specified vbmeta image headers. If an image has a vbmeta footer,
/// then an error is returned because the vbmeta patching logic only ever writes
/// root vbmeta images.
@@ -723,7 +823,7 @@ fn update_vbmeta_headers(
//
// The root vbmeta image is always signed because it is possible to
// invoke avbroot is a way that no modifications are made (rootless +
// skipping recovery otacerts.zip patch). We still want the result to be
// skipping both otacerts.zip patches). We still want the result to be
// bootable.
if parent_header != &orig_parent_header || name == "vbmeta" {
parent_header
@@ -735,13 +835,9 @@ fn update_vbmeta_headers(
let mut writer = tempfile::tempfile()
.with_context(|| format!("Failed to create temp file for: {name}"))?;
parent_header
.to_writer(&mut writer)
avb::write_root_image(&mut writer, parent_header, block_size)
.with_context(|| format!("Failed to write vbmeta image: {name}"))?;
padding::write_zeros(&mut writer, block_size)
.with_context(|| format!("Failed to write vbmeta padding: {name}"))?;
let input_file = images.get_mut(name).unwrap();
input_file.file = Arc::new(writer);
input_file.state = InputFileState::Modified;
@@ -909,6 +1005,7 @@ fn patch_ota_payload(
payload: &(dyn ReadAt + Sync),
writer: impl Write,
external_images: &HashMap<String, PathBuf>,
re_sign_images: &HashSet<String>,
boot_patchers: &[Box<dyn BootImagePatch + Sync>],
skip_system_ota_cert: bool,
clear_vbmeta_flags: bool,
@@ -949,6 +1046,12 @@ fn patch_ota_payload(
}
}
for name in re_sign_images {
if !all_partitions.contains(name.as_str()) {
bail!("Cannot re-sign non-existent {name} partition");
}
}
let required_images = get_required_images(&header.manifest, required_flags);
let vbmeta_images = required_images
.iter()
@@ -970,6 +1073,7 @@ fn patch_ota_payload(
payload,
&required_images,
external_images,
re_sign_images,
&header,
cancel_signal,
)?;
@@ -994,6 +1098,14 @@ fn patch_ota_payload(
)?)
};
re_sign_unmodified_images(
re_sign_images,
&mut input_files,
key_avb,
header.manifest.block_size().into(),
cancel_signal,
)?;
let mut vbmeta_headers = load_vbmeta_images(&mut input_files, &vbmeta_images)?;
ensure_partitions_protected(&required_images, &vbmeta_headers)?;
@@ -1129,6 +1241,7 @@ fn patch_ota_zip(
zip_reader: &ZipArchive<ReaderAtWrapper<&File>>,
zip_writer: &mut ZipArchiveWriter<impl Write>,
external_images: &HashMap<String, PathBuf>,
re_sign_images: &HashSet<String>,
boot_patchers: &[Box<dyn BootImagePatch + Sync>],
skip_system_ota_cert: bool,
clear_vbmeta_flags: bool,
@@ -1295,6 +1408,7 @@ fn patch_ota_zip(
&payload_reader,
&mut data_writer,
external_images,
re_sign_images,
boot_patchers,
skip_system_ota_cert,
clear_vbmeta_flags,
@@ -1519,6 +1633,8 @@ pub fn patch_subcommand(cli: &PatchCli, cancel_signal: &AtomicBool) -> Result<()
external_images.insert(name.to_owned(), path.to_owned());
}
let re_sign_images = cli.re_sign.iter().cloned().collect::<HashSet<_>>();
let mut boot_patchers = Vec::<Box<dyn BootImagePatch + Sync>>::new();
if let Some(magisk) = &cli.root.magisk {
@@ -1582,6 +1698,7 @@ pub fn patch_subcommand(cli: &PatchCli, cancel_signal: &AtomicBool) -> Result<()
&zip_reader,
&mut zip_writer,
&external_images,
&re_sign_images,
&boot_patchers,
cli.skip_system_ota_cert,
cli.clear_vbmeta_flags,
@@ -2276,6 +2393,19 @@ pub struct PatchCli {
)]
pub replace: Vec<OsString>,
/// Re-sign unmodified partition image.
///
/// Modified partition images are always re-signed. This option forces the
/// specified image to be re-signed, even if it was unmodified. When used
/// with --replace, a copy of the replacement image will be re-signed, not
/// the original file.
///
/// This option only has an effect if the specified partition image is
/// originally signed. If it is unsigned because the AVB descriptors are
/// embedded in another signed vbmeta image, then this option is a no-op.
#[arg(long, value_names = ["PARTITION"], help_heading = HEADING_PATH)]
pub re_sign: Vec<String>,
#[command(flatten)]
pub root: RootGroup,
+1 -1
View File
@@ -211,7 +211,7 @@ impl MagiskRootPatcher {
// replaced by PREINITDEVICE
// - Versions newer than the latest supported version are assumed to support
// the same features as the latest version
const VERS_SUPPORTED: &'static [Range<u32>] = &[25102..25207, 25211..30700];
const VERS_SUPPORTED: &'static [Range<u32>] = &[25102..25207, 25211..30800];
const VER_PREINIT_DEVICE: RangeFrom<u32> = 25211..;
const VER_RANDOM_SEED: Range<u32> = 25211..26103;
const VER_PATCH_VBMETA: Range<u32> = Self::VERS_SUPPORTED[0].start..26202;
+2 -2
View File
@@ -53,11 +53,11 @@ data.ramdisks = [["otacerts", "first_stage", "dsu_key_dir"]]
[profile.pixel_v4_gki.hashes_streaming]
original = "ea96196191e3a4133db4aff45d47aa3468514e29e0a724faac8081cbf4adf808"
patched = "179923431ff94148186d8b0a457636c8a60057d047d55a4fc4da3a162e73eeed"
patched = "c073cbb271099aaf338a8c70f3565ec70db47a87b4c9bb3efffd96ccee1a0c27"
[profile.pixel_v4_gki.hashes_seekable]
original = "f6615ae355eba38689d24aa535981d09175d4832e7c65c04cdd89aa95d21f09d"
patched = "b006858b78cddef0e3db1d77454cd945111ce15708121e3a483ff711abedec8b"
patched = "65ce91a95574a308ffc5279696efe7f6f7e4e847795dc9ae43d9ffd012a7ce3f"
# Google Pixel 6a
# What's unique: boot (boot v4, no ramdisk) + vendor_boot (vendor v4, 2 ramdisks)
+3 -1
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2023-2025 Andrew Gunnerson
// SPDX-FileCopyrightText: 2023-2026 Andrew Gunnerson
// SPDX-FileCopyrightText: 2023 Pascal Roeleven
// SPDX-License-Identifier: GPL-3.0-only
@@ -1067,6 +1067,8 @@ fn patch_image(
OsStr::new("--replace"),
OsStr::new("system"),
system_image_file.as_os_str(),
OsStr::new("--re-sign"),
OsStr::new("boot"),
OsStr::new("--key-avb"),
avb_key_file.as_os_str(),
OsStr::new("--pass-avb-file"),