241 Commits

Author SHA1 Message Date
Andrew Gunnerson 65979beff4 Version 2.0.2
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
v2.0.2
2023-09-06 23:16:01 -04:00
Andrew Gunnerson 47cf015f15 CHANGELOG.md: Add entry for PR #137
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-06 23:15:19 -04:00
Andrew Gunnerson e40e036159 Merge pull request #137 from chenxiaolong/anyhow
Remove unnecessary use of anyhow macro
2023-09-06 23:13:04 -04:00
Andrew Gunnerson 0d2a158ead Remove unnecessary use of anyhow macro
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-06 23:04:18 -04:00
Andrew Gunnerson ddabb19b4a CHANGELOG.md: Add entry for PR #136
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-06 22:51:58 -04:00
Andrew Gunnerson 3c05eb300c Merge pull request #136 from chenxiaolong/offset
PayloadWriter: Only set data_offset for operations that reference blobs
2023-09-06 22:50:31 -04:00
Andrew Gunnerson 2ffb1dfdbd PayloadWriter: Only set data_offset for operations that reference blobs
This fixes `data_offset` being set for `ZERO` and `DISCARD` operations,
which prevents some images (eg. `ossi`) from being flashed due to
update_engine's strict field validation.

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-06 22:40:15 -04:00
Andrew Gunnerson f93dc55f70 CHANGELOG.md: Add entry for PR #135
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-06 20:05:41 -04:00
Andrew Gunnerson 393e65f6b4 Merge pull request #135 from chenxiaolong/full
Move full OTA check to patch/extract subcommand function
2023-09-06 20:03:47 -04:00
Andrew Gunnerson 3f86a67038 Move full OTA check to patch/extract subcommand function
This way, the payload-related functions can be used to parse incremental
OTA files.

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-06 19:51:48 -04:00
Andrew Gunnerson 4337170f57 Version 2.0.1
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
v2.0.1
2023-09-04 17:11:28 -04:00
Andrew Gunnerson a73365592f CHANGELOG.md: Add entry for PR #132
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-04 16:41:28 -04:00
Andrew Gunnerson 0018e8a5ba Merge pull request #132 from chenxiaolong/magisk
boot.rs: Allow Magisk 263xx
2023-09-04 16:39:14 -04:00
Andrew Gunnerson adf0014da2 boot.rs: Allow Magisk 263xx
There are no changes that break avbroot.

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-09-04 16:30:39 -04:00
Andrew Gunnerson 1179f9f2fa Version 2.0.0
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
v2.0.0
2023-08-31 23:43:22 -04:00
Andrew Gunnerson bbe793bd2d Merge pull request #130 from chenxiaolong/rust
avbroot 2.0: Rewrite in Rust
2023-08-31 23:42:34 -04:00
Andrew Gunnerson d643b51579 xtask: Update changelog version in set-version
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-08-31 20:51:13 -04:00
Andrew Gunnerson a25d9a9eb4 Update dependencies
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-08-31 20:49:31 -04:00
Andrew Gunnerson b92513609b Move main avbroot code to a workspace member
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-08-31 20:49:29 -04:00
Andrew Gunnerson 44d62f5147 Add release management tasks
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-08-30 19:23:07 -04:00
Andrew Gunnerson fb4b93b403 ota verify: Add check for the ramdisk's otacerts.zip
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-08-29 19:25:27 -04:00
Andrew Gunnerson c6ac508a50 Work around upstream bzip2 infinite loop issue
There's an upstream bug that causes an infinite loop in the
`write::BzDecoder` destructor if the decoder is fed invalid data. While
this never happens during normal operation, it is possible to run into
this by running `ota extract` against a `--stripped` OTA file.

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-08-29 19:23:50 -04:00
Andrew Gunnerson 6d892d37ff Add support for Magisk v26.2
There is nothing new that requires changes on the avbroot side.

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-08-29 16:25:05 -04:00
Andrew Gunnerson 8549fa1dfc avbroot 2.0: Rewrite in Rust
Why?
----

It was always my intention to write avbroot in a compiled language.
Python was a stop-gap solution since it was possible to use the various
tools and parsers from AOSP to make the initial prototyping and
implementation easier. However, doing so required a whole lot of hacks
since nearly all of the Python modules we use were intended to be used
as executables, not libraries, and they were definitely not meant to be
used outside of AOSP's code base.

Although the dependencies on AOSP code have been reduced over time,
working on the Python code is still frustrating. The majority of the
modules we use from both the standard library and external dependencies
are lacking type annotations. All of the Python language servers and
type checker tools I've used choked on them. There have been serveral
avbroot bugs in the past that wouldn't have happened with any
statically typed language.

The catalyst for me working on this recently was dealing with some
python-protobuf versions that wouldn't work with AOSP's pregenerated
protobuf bindings. When parsing protobuf messages, it would fail
with obscure runtime type errors. I need my projects to not feel
frustrating or else I'll just get burnt out.

Hence, the Rust rewrite. With fewer hacks this time! avbroot no longer
has any dependencies on external tools like openssl. I'll be providing
precompiled binaries for the three major desktop OS's, built by GitHub
Actions. avbroot will also be versioned now, starting at 2.0.0.

Whats new?
----------

* A new `avbroot ota verify` subcommand has been added to check that all
  OTA and AVB related components have been properly hashed and signed.
  This works for all OTA images, including stock ones.
* A couple new `avbroot avb` subcommands have been added for dumping
  vbmeta header/footer information and verifying AVB signatures. These
  are roughly equivalent to avbtool's `info_image` and `verify_image`
  subcommands, though avbroot is about an order of magnitude faster than
  the latter.
* A new set of `avbroot boot` subcommands have been added for packing
  and unpacking boot images. It supports Android v0-v4 images and vendor
  v3-v4 images. Repacking is lossless even when using deprecated fields,
  like the boot image v4 VTS signature.
* A new `avbroot ramdisk` subcommand has been added for inspecting
  the CPIO structure of ramdisks.
* A new set of `avbroot key` subcommands have been added for generating
  signing keys so that it's no longer necessary to install openssl and
  avbtool (though of course, keys generated by other tools remain fully
  compatible).
* Since avbroot has a ton of CLI options, a new `avbroot completion`
  subcommand has been added for generating tab-completion configs for
  various shells (eg. bash, zsh, fish, powershell).

What was removed?
-----------------

Nothing :) The `patch` and `extract` subcommands have been moved under
`avbroot ota` and the `magisk-info` subcommand has been moved under
`avbroot boot`, but there are compatibility shims in place to keep all
the old commands working.

The command-line interface will remain backwards compatible for as long
as possible, even with new major releases. The Rust API, however, has no
backwards compatibility guarantees. I currently don't intend for
avbroot's "library" components to be used anywhere outside of Custota
and avbroot itself.

Performance
-----------

Due to having better access to low-level APIs (especially `pread` and
`pwrite`), nearly everything that can be multithreaded in avbroot is now
multithreaded. In addition, during the patching operation, everything
is done entirely in memory without temp files and the maximum memory
usage is still about 100MB lower than with the Python implementation.

The new implementation is bottlenecked by how fast a single CPU core can
calculate 3 SHA256 hashes of overlapping regions spanning the majority
of the OTA file. About 90% of the CPU time is spent calculating SHA256
hashes and another 5% or so performing XZ-compression.

Some numbers:

* Patching should take roughly 40%-70% of the time it took before.
* Extracting with `--all` should take roughly 10%-30% of the time it
  took before.

Folks with x86_64 CPUs supporting SHA-NI extensions (eg. Intel 11th gen
and newer) should see even bigger improvements.

Reproducibility
---------------

The new implementation's output files are bit-for-bit identical when the
inputs are the same. However, they do not exactly match what the Python
implementation produced.

* The zip entries, aside from `metadata` and `metadata.pb`, are written
  in sorted order.
* All zip entries are stored without compression.
* All zip entries are stored without additional metadata (eg.
  modification timestamp).
* The OTA certificate, both in the OTA zip and in the recovery ramdisk's
  `otacerts.zip`, goes through deserialization + serialization before
  being written. Text in the certificate file before the header and
  after the footer will be stripped out.
* The protobuf structures (payload header and OTA metadata) are
  serialized differently. Protobuf has more than one way to encode the
  same messages "on the wire". The Rust quick_protobuf library
  serializes messages a bit differently than python-protobuf, but the
  outputs are mutually compatible.
* XZ compression of modified partition images in the payload is now done
  at compression level 0 instead of 6. This reduces the patching time by
  several seconds at the cost of a couple MiB increase in file size.
* Ramdisks are now compressed with standard LZ4 instead of LZ4HC (high
  compression mode). For our use case, the difference is <100 KiB, but
  using standard LZ4 allows us to use a pure-Rust LZ4 library and makes
  the compression step much faster.
* Older ramdisks compressed with gzip are slightly different due to a
  different gzip implementation being used (flate2 vs. zlib). The two
  implementations structure the gzip frames slightly differently, but
  the output is identical when decompressed.
* Magisk's config file in the ramdisk (`.backup/.magisk`) will have the
  `SHA1` field set to all zeros. This allows avbroot to keep track of
  less information during patching for better performance. The field is
  only used for Magisk's uninstall feature, which can't ever be used in
  a locked bootloader setup anyway.

Misc
----

While working on the new `avbroot ota verify` subcommand, I found that
the `ossi` stock image (OnePlus 10 Pro) used in avbroot's tests has an
invalid vbmeta hash for the `odm` partition. I thought it was an avbroot
bug, but AOSP's avbtool reports the same invalid hash too. If that image
actually boots, then I'm not sure AVB can be trusted on those devices...

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-08-29 15:54:53 -04:00
Andrew Gunnerson ac80abe640 Merge pull request #125 from chenxiaolong/comment
avbroot/ota.py: Fix incorrect comment about the absolute signature offset
2023-08-11 15:28:11 -04:00
Andrew Gunnerson 84165287cf avbroot/ota.py: Fix incorrect comment about the absolute signature offset
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-08-11 15:27:37 -04:00
Andrew Gunnerson 5cad5d0350 Merge pull request #124 from chenxiaolong/actions
Add GitHub Actions workflow to build modules
2023-08-11 15:26:23 -04:00
Andrew Gunnerson 4db33db175 Add GitHub Actions workflow to build modules
Fixes: #123

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-08-11 15:17:22 -04:00
Andrew Gunnerson c92ee1c666 Merge pull request #122 from chenxiaolong/umask
Make output file permissions respect umask
2023-08-07 16:10:29 -04:00
Andrew Gunnerson 55d64cbcee Make output file permissions respect umask
We have to emulate this with fchmod because Python's NamedTemporaryFile
always opens the file descriptor with 600 permissions.

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-08-07 15:59:53 -04:00
Andrew Gunnerson 3e9fad0422 Merge pull request #120 from chenxiaolong/custota
README.md: Mention Custota as an alternative to the clearotacerts module
2023-08-03 13:24:37 -04:00
Andrew Gunnerson bbd547e5e4 README.md: Mention Custota as an alternative to the clearotacerts module
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-08-03 13:06:38 -04:00
Andrew Gunnerson 7d2a103659 Merge pull request #114 from chenxiaolong/zip
Ensure output files are zip64-compliant (again)
2023-07-03 18:19:30 -04:00
Andrew Gunnerson bf53859bcd Ensure output files are zip64-compliant (again)
PR #110 fixed this incorrectly. I misinterpreted "set to zero in the
local header" from §4.4.4 in the spec as referring to the two 32-bit
size fields in the local header. However, the spec considers the zip64
extra record as part of the local header. The correct behavior for
writing zip64 entries to unseekable files is 0xffffffff in the 32-bit
local header size fields (to enable zip64), 0 in the zip64 extra record
(due to streaming writes), and the actual sizes in the data descriptor.

Fixes: #109

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-07-03 16:30:09 -04:00
Andrew Gunnerson 056359c853 Merge pull request #103 from chenxiaolong/replace
Add support for replacing any partition image
2023-06-28 21:24:50 -04:00
Andrew Gunnerson 93dcd63829 Add support for replacing any partition image
This commit adds support for replacing any partition image within
the payload with a custom image. This is useful, for example, to add a
custom kernel to an OTA, which may involve partitions that wouldn't
normally be touched (eg. `vendor_dlkm`).

Any image specified via `--replace` will have its corresponding
descriptor in the vbmeta image updated. This is handled recursively. For
example, replacing `vendor_dlkm` would update both `vbmeta_vendor` and
`vbmeta`. This requires all vbmeta images to be extracted during the
patching process so that a complete dependency graph can be computed.
The performance hit in doing so is negligible, but does require the
checksums of the stripped images to be updated for the tests.

Fixes: #102

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-06-28 21:04:49 -04:00
Andrew Gunnerson be177d0111 Merge pull request #112 from chenxiaolong/ignore_prepatched
Add support for ignoring boot image checks for --prepatched
2023-06-28 20:28:24 -04:00
Andrew Gunnerson 48318f333a Add support for ignoring boot image checks for --prepatched
There are 3 levels of warnings:

* Level 0: Warnings that don't affect booting
    * Mismatched `id` or `os_version` fields
* Level 1: Warnings that may affect booting
    * Mismatched `cmdline` or `extra_cmdline` fields
    * Unexpected addition of `kernel`, `second`, `recovery_dtbo`, `dtb`,
      or `bootconfig`
* Level 2: Warnings that are very likely to affect booting
    * All other mismatched, added, or removed fields

By default, any warning of level 1 or higher is treated as a fatal
error. Each time `--ignore-prepatched-compat` is passed in, the
permitted warning level is increased.

Fixes: #108

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-06-28 19:40:50 -04:00
Andrew Gunnerson 1b5bc24821 Merge pull request #111 from chenxiaolong/arch
tests: Containerfile.arch: Allow system-wide pip installation
2023-06-28 19:21:22 -04:00
Andrew Gunnerson a0421d3eb4 tests: Containerfile.arch: Allow system-wide pip installation
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-06-28 18:59:21 -04:00
Andrew Gunnerson fec788c438 Merge pull request #110 from chenxiaolong/zip64
Ensure output files are zip64-compliant
2023-06-28 18:58:14 -04:00
Andrew Gunnerson f25ac50b0a Ensure output files are zip64-compliant
This commit fixes two issues:

* Python 3.11.4 has a regression where it sets the local file header's
  compressed and uncompressed size fields to 0xffffffff when data
  descriptors are used. These should be set to 0 (along with the CRC)
  according to §4.4.4 of the spec. We work around this by monkey
  patching zipfile's local file header serialization function to correct
  the fields.
* avbroot tries to preserve as much metadata from the original zip as
  possible, including the `extra` records. This caused zip64 records
  (0x0001) to be duplicated. The first instance was from the original
  zip (containing invalid size fields) and the second instance was newly
  created by zipfile. We fix this by stripping out all 0x0001 records,
  similar to what we already do for 0xd935.

Fixes: #109

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-06-28 18:38:06 -04:00
Andrew Gunnerson 2bc69f5bc6 Merge pull request #107 from chenxiaolong/readme
README.md: Improve project description and remove suggestion for compiling AOSP from source
2023-06-22 19:57:28 -04:00
Andrew Gunnerson 1b5d98d718 README.md: Improve project description and remove suggestion for compiling AOSP from source
Issue: #106

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-06-22 19:56:23 -04:00
Andrew Gunnerson 5b1622c382 Merge pull request #105 from chenxiaolong/windows
modules/build.py: Windows requires `.bat` extension to be specified explicitly
2023-06-14 18:05:38 -04:00
Andrew Gunnerson 6c7165cfb7 modules/build.py: Windows requires .bat extension to be specified explicitly
Fixes: #104

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-06-14 18:04:47 -04:00
Andrew Gunnerson 5dda181e90 Merge pull request #101 from chenxiaolong/readme
README.md: Add note about using unencrypted private keys
2023-05-31 00:05:27 -04:00
Andrew Gunnerson 0ff1bb2b2f README.md: Add note about using unencrypted private keys
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-05-31 00:04:48 -04:00
Andrew Gunnerson 19bfc0bd8e Merge pull request #100 from chenxiaolong/env_var
Add support for supplying the private key passphrases non-interactively
2023-05-31 00:04:04 -04:00
Andrew Gunnerson 6be4665557 Update tests for non-interactive patching with encrypted keys
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-05-30 23:54:16 -04:00