RAUC: (R)evolution of an Update Framework
Enrico Jörns, Pengutronix [Open Source Summit EU 2022]

RAUC is an update system. It creates the update image, downloads it from a server or a storage device, and applies it in a fail-safe way. It uses A/B updates. Updates are verified cryptographically before applying.

Recently a new “verity” bundle format was created which solves some security issues, makes things faster, and allows streaming.

System integration on target defines how updates are done and how partitioning is done. There’s also a configuration of the update bundle that specifies which parts are updated (e.g. only kernel, or also the bootloader).

For verification, the artifact is signed, not the channel. So it can be downloaded from anywhere or be in a USB stick.

The initial file format used by rauc was a squashfs of the components + manifest, then appending the signature using CMS container. The signature is used to verify the squashfs as a whole. squashfs is used because it can be mounted directly, doesn’t need to be read in memory.

Problem with this approach is that you can only verify the squashfs when you have the entire filesystem available. In addition, there’s a TOCTOU race: it’s possible to modify the file after the signature check was done either before or during the mount.

To improve this, the verity bundle format was created. It is basically a dm-verity on top of the squashfs. Use of this format is specified in the bundle manifest. In this format, only the manifest which includes the root hash is signed. Only that is verified with CMS, which is much quicker than verifying the squashfs. The verification is done by the kernel on demand, and every time something is read.

This bundle format also opens the possibility of streaming. The implementation is done with an unprivileged helper that sits between dm-verity and the actual contents. Instead of a file, the contents is read with HTTP Range requests. So even though the signature and verity information is at the end of the file, it can be accessed in the beginning and on demand.

Delta updates are a way to make the update files smaller. They of course only work for incremental updates. rauc wants to make this better with adaptive updates. The bundle itself is still a full update, but with the streaming, it doesn’t need to be downloaded completely. Instead, some metadata is added to the bundle that specifies which parts of the bundle are actually needed for this specific update. This makes it possible to update from any version using the same bundle. This has not been implemented fully yet, only the format is specified and implemented.

The adaptive format splits the image into blocks and hashes each block. The block hashes are compared with what already exists in flash, and if it’s the same, it’s not updated. This works well with ext4, but not with e.g. squashfs. A similar mechanism exists for the tree-rsync update mechanism, where it is possible to make hashes per file.

Encryption was not implemented with the original format, it has been added with the verity format. It is split up into two steps because often the build server doesn’t have the keys, those are in a HSM. The bundle is first generated with an ephemeral key for dm-crypt and this key is stored in the manifest. The rauc encrypt command takes the manifest and encrypts it and adds it in CMS format. This makes it possible to have different symmetric or asymmetric keys for different recipients. Combining it with dm-verity gives authenticated encryption, and since it’s all block based it can be streamed.

The new format also allows you to add any custom information to the bundle manifest file.

An under development feature is to have configurable logging of which updates have been done.