Commit Graph

36 Commits

Author SHA1 Message Date
Andrew Gunnerson 6aacc5a76c Update dependencies
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2025-08-06 21:33:54 -04:00
Andrew Gunnerson 182d937d34 Update all dependencies
This also fixes a number of disabled-by-default clippy warnings and
updates the Rust edition to 2024.

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2025-05-04 18:06:59 -04:00
Andrew Gunnerson f2e47a65d4 Switch back to ring
This reverts commit e929ecbe44.

Ring is back to being maintained again, so let's switch back to it since
it has fewer build dependencies and is much faster to compile.

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2025-04-05 23:52:53 -04:00
Andrew Gunnerson 796e2a4fa2 Update dependencies
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2025-03-23 19:17:15 -04:00
Andrew Gunnerson a62d0a5c91 Update dependencies
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2025-03-12 18:25:35 -04:00
Andrew Gunnerson e929ecbe44 Replace ring with aws-lc
The author of ring recently announced that the library is no longer
being maintained and fixes for security issues may be significantly
delayed. Big thanks to Brian Smith for creating and maintaining the
library for so long!

This commit replaces ring with aws-lc, a cryptography library maintained
by Amazon AWS. It seems to be well-regarded and is used by high-profile
projects like rustls. It is also API-compatible with ring, so it is
effectively a drop-in replacement.

Unfortunately, we still cannot switch back to the RustCrypto SHA1 and
SHA2 implementations because they are still significantly slower than
ring and aws-lc on systems that do not support the SHA-NI extensions.

https://rustsec.org/advisories/RUSTSEC-2025-0007

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2025-02-21 20:44:19 -05:00
Andrew Gunnerson 80d5f19223 Update dependencies
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2025-01-26 18:10:42 -05:00
Andrew Gunnerson 2d4f08f48b Update dependencies
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2024-12-25 19:56:02 -05:00
Andrew Gunnerson b351d47f28 Update dependencies and fix most pedantic clippy warnings
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2024-12-08 18:46:40 -05:00
Andrew Gunnerson 99c5800f96 Update dependencies and pin Github Actions actions to specific commits
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2024-12-01 17:42:35 -05:00
Andrew Gunnerson 5a7885674e ci.yml: Use target name in Rust cache key
Otherwise, the x86_64-unknown-linux-gnu build running on Ubuntu 22.04
can use the cache from the aarch64-linux-android31 build that originally
ran on Ubuntu 24.04. This fails due to some cached components having
been compiled against a newer version of glibc.

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2024-09-22 21:13:33 -04:00
Andrew Gunnerson 8e52a9cf8c Add support for cross-compiling to Android
The precompiled binaries are compiled for aarch64 API 31, which should
work for every device that avbroot supports.

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2024-06-14 17:49:15 -04:00
Andrew Gunnerson ae0863e319 ci.yml: Build universal binary for macOS
Fixes: #278

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2024-05-02 19:10:16 -04:00
Andrew Gunnerson af0fb1d9b4 Update Github Actions dependencies
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2024-04-14 18:21:54 -04:00
Andrew Gunnerson a134d7f889 Remove oemunlockonboot module
The module has been split out into another repo [1] so that it can be
versioned separately. It now also supports Magisk's automatic update
mechanism.

Closes: #235

[1] https://github.com/chenxiaolong/OEMUnlockOnBoot

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-12-30 19:16:05 -05:00
Andrew Gunnerson 3a3582ce4c e2e: Switch to using mock OTAs for testing
This commit replaces the previous approach of patching real OTAs with
patching mock OTAs. The motivation for this change is to make it
possible to test the system partition otacerts.zip patching without
needing to download huge files. Adding the system image to the stripped
OTAs would increase the file size by an order of magnitude.

The mock OTAs are generated from a set of profiles defined in e2e.toml.
The four included profiles are meant to mimic the OTAs used for testing
before:

* pixel_v4_gki     ~= cheetah
* pixel_v4_non_gki ~= bluejay
* pixel_v3         ~= bramble
* pixel_v2         ~= sunfish

There is no equivalent profile for ossi because newer OnePlus devices no
longer support custom signing keys properly.

The mock OTAs are perfectly valid, structure and signature-wise. They
just don't include any real partition data where possible. They are
initially signed with a different set of keys to ensure that the changes
made by the patching process are actually visible.

With how small the mock OTAs are, testing every profile only takes about
two seconds. Thus, the Github Actions workflow was adjusted to just run
e2e in the same job as the build.

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-12-29 16:07:57 -05:00
Andrew Gunnerson 83218a747d workflows/ci: Fix overwriting symlink for target/output
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-12-12 21:19:26 -05:00
Andrew Gunnerson e097f98404 workflows/ci: Build statically linked executables where possible
This way, the precompiled executables can run on distros that ship an
older version of glibc or don't use glibc at all.

Fixes: #222

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-12-12 18:34:02 -05:00
Andrew Gunnerson d5605eabc0 workflows/ci: Use Rust triple instead of LLVM triple
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-12-12 18:05:00 -05:00
Andrew Gunnerson 6491106df0 Add names to all Github Actions workflows
Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-10-01 19:33:58 -04:00
Andrew Gunnerson d69fdaadae ci.yml: Only cancel previous runs for pull requests
Fixes: #177

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-10-01 19:09:58 -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 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 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
Pascal Roeleven 9d50d41f6b Give names to reusable workflows 2023-03-09 19:57:02 +01:00
Pascal Roeleven d53896f952 Use actions/cache lookup-only instead of custom action
Now that the lookup-only option is available, we should use this instead
2023-03-09 19:57:00 +01:00
Pascal Roeleven df9a6c2970 Make cache pre-checks more robust
Also fix preload Magisk cache workflow
2023-03-06 12:18:57 +01:00
Andrew Gunnerson 4847ebf27a Merge tests_ci into tests
This commit merges the CI tests from `tests_ci/` into `tests/` so we
have a single way to test against both full OTAs and stripped/dummy
OTAs.

Features kept from tests_ci:

* Support for dummy OTAs. They're now called stripped OTAs to hopefully
  better signify that they're a stripped down version of the original
  file instead of a test file created from scratch.
* Extracting AVB partitions and verifying their hashes.
* Writing sparse files where possible.
* Ability to change the working directory for downloads and output
  files.

Test script changes:

* There are now 4 subcommands:
    strip -i <input> -o <output>
    add -u <url> -d <device> [-H <expected hash>]
    download [--magisk | --no-magisk] [[-d <device>] ... | --no-devices]
    test [[-d <device>] ...]
* Downloads now use the parallel downloader for both full and stripped
  OTAs.

Config file changes:

* It now uses strictyaml instead of configparser/TOML since it's a bit
  more readable now that we have more nesting in the data structure. The
  config loader now also makes use of strictyaml's schema feature.
* All hashes are now SHA-256 for consistency.
* For stripped OTAs, the list of byte ranges now uses half-open
  intervals for easier calculations.
* Use device IDs as the key instead of the marketing model name.

CI changes:

* Compute all cache keys (and prefixes) in the main workflow and pass
  them to the preload scripts.
* Only run on `push` for the `master` branch to avoid double workflow
  runs on PRs from internal branches.
* Increase timeout for patching tests because `tests.py` patches twice
  (once with `--magisk` and once with `--prepatched`).

Signed-off-by: Andrew Gunnerson <accounts+github@chiller3.com>
2023-03-04 21:39:08 -05:00
Pascal Roeleven e9419850f5 Run workflow on pull requests 2023-03-03 16:00:25 +01:00
Pascal Roeleven 9dec3f8408 Use sparse images in GA
Also invalidate caches by adding version prefix
2023-03-02 15:22:34 +01:00
Pascal Roeleven f8ef998794 Reorder steps 2023-03-01 16:50:12 +01:00
Pascal Roeleven 4ac7826c32 Cache tox environments with tox hash 2023-03-01 16:11:32 +01:00
Pascal Roeleven 50b8d28779 Cache images with database hash 2023-03-01 16:04:14 +01:00
Pascal Roeleven 0b6f76637f Move caching of Magisk to setup workflow
It's unnecessary to spin up an entire runner for just this short and
simple task.
2023-03-01 16:04:14 +01:00
Pascal Roeleven 41ce7d653f Cache Magisk with database hash
Simplify caching by caching on database hash instead of Magisk hash.
hashFiles is only available in the steps, hence the reason for it being
exported to the outputs from the steps (and not directly).
2023-03-01 16:03:59 +01:00
Pascal Roeleven fb2c512248 Test all device images in Github Actions 2023-02-27 16:38:39 +01:00