Guardians of the Bootchain: Unpacking Verified Boot in Android’s Security Architecture.
We’ve reached the fourteenth stage of our journey through the deep, hidden, and intricate world of the Android OS. This is the second in a series of three articles about "Android's Security Model".
After exploring the world of sandboxing and permissions in our previous article "Inside the Android Fortress" (1.) we now shift our focus to one of the most foundational pillars of Android’s defense: Verified Boot.
This mechanism is the first to be activated each time an Android device is turned on. Long before any app runs or system service loads, Verified Boot is already at work, ensuring that the software launching on your device is trusted, untampered, and secure.
In this article, we’ll walk through each stage of the Android boot process, from the hardware root of trust and bootloaders, to the Android Verified Boot (AVB) process and dm-verity in the Linux kernel. We’ll explore how these components work together to form a cryptographic chain of trust that shields Android from persistent rootkits, modified system images, and other low-level threats.
By the end of this article, you’ll not only understand how Verified Boot works under the hood, but also how to implement it yourself, from selecting a secure System-on-Chip (SoC), to burning cryptographic keys, and configuring your AOSP build for full verified boot support.
Whether you're a system developer, security enthusiast, or just curious about how Android protects itself even before the OS starts, this journey will show you the inner workings of one of Android’s most critical, yet often invisible, security mechanisms.
Let’s start by getting a first taste of Verified Boot, just a quick look on the surface. Then, step by step, we’ll dive deeper into the topic, exploring the hidden corners of the source code.
✏️📄 Verified Boot in Android: Explained Simply.
Verified Boot is an important security feature in Android. It checks that the system has not been changed or tampered with when the device starts up. Its main job is to make sure only trusted and safe code runs on your device. It works using a chain of trust, starting from the hardware all the way to the Android operating system. At each step, the system checks that everything is real and hasn’t been altered, using digital signatures.
The process begins with a small piece of software called the bootloader, which lives in a special read-only part of the device. This bootloader is signed by the manufacturer, and its signature is checked using a public key stored in the hardware. When the device powers on, the first bootloader checks the signature of the next one. If everything looks good, the system continues loading. But if something is wrong, the boot stops, which helps block malicious software.
Once the system is running, it also checks that important files and system apps haven’t been changed. Every part checks the next, creating a chain of trust. One big benefit of Verified Boot is that it prevents rollback attacks. That’s when someone tries to install an old, less secure version of Android to hack the device. Verified Boot keeps track of the newest approved version and won’t let the device start with an older one. It also checks for any changes in system files. If something was modified, the system will stop booting to keep things safe.
In short, Verified Boot is a key part of Android’s security. It makes sure only safe software runs, blocks old insecure versions, and detects any tampering. While the process is complex and articulated, its goal is simple: keep your device safe and trustworthy.
So let's start with a short story that is a metaphor for this topic.
Here's another episode of the Android Corporation saga!
We’re back in the Security Team office, where Captain Verified Boot shows off his skills and saves Android Corp from a sneaky intruder.
🦸♂️♂️ Captain Verified Boot Saves the Morning.
It’s early morning at the Android Security Office. The team is sipping coffee when suddenly the alarm lights flash red. The system has detected a boot-time anomaly. Something, or someone, is trying to sneak into the office before opening hours.
Captain Verified Boot straightens his uniform, eyes narrowing. “This is my turf,” he says calmly. “If something shady is trying to get in before the system even wakes up, it has to go through me.”
Down in the boot chamber, a suspicious suitcase labelled modified_boot.img tries to roll past the entrance scanner. But the scanner doesn't blink. Captain Verified Boot steps forward, scans the suitcase, and immediately raises a shield.
“Unauthorized boot image detected. Signature mismatch. Halt!”
Apparently, an attacker attempted to replace the official Android boot image with a tampered one, presumably to install a rootkit before the system was even up and running. But thanks to Verified Boot, the digital signatures do not match those stored in the hardware root of trust tamper-proof fuses.
Back in the control room, Officer SELinux 👮 checks the logs. “The system never got a chance to boot compromised code. Nice work, Captain.”
Dr. SandBox 👨🔬 intervenes: “If that malware had been insinuated during boot, it would’ve been invisible even to me. Your shield caught it at the only point it could be stopped.”
Permission Manager 👩💼 nods. “And if the boot’s not trusted, none of my runtime checks can be trusted either. This saved the entire policy framework.”
Captain Verified Boot smiles modestly. “Just doing my job. I don’t let the OS start until I’m sure every layer below is exactly as it should be.”
The lights turn green again. The office resumes normal operations.
And so, thanks to Captain Verified Boot, the Android Security Office lives to defend another day safe, clean, and uncompromised.
Before diving into implementation, it's crucial to understand that Verified Boot isn't a single component but rather an ecosystem of security features working together. At its core, it combines: Cryptographic verification of all boot components, Hardware-backed security through TEE (Trusted Execution Environment), Runtime integrity checks via dm-verity e Secure key management throughout the boot process.
The magic happens when these elements work in harmony to ensure only authorized software can run on the device.
Well, let's see it:
🔐 Verified Boot in Android: Let’s dive a little deeper.
Starting with Android 7, all official Android devices are required to support Verified Boot. This feature is designed to make sure your phone or tablet only runs trusted software. In other words, it protects your device from low-level tampering or corruption by checking that every part of the system has not been changed in an unauthorized way (4.).
Verified Boot combines two powerful security techniques:
A secure boot chain built into the device’s hardware (silicon).
dm-verity, a Linux kernel feature that checks system integrity at runtime.
🔐 How Secure Boot Works.
At the heart of Verified Boot is the boot chain, a series of steps that load the operating system. Each step in the chain verifies the integrity of the next one before passing control. It all starts with the device’s chipset, which stores a set of cryptographic keys inside one-time write memory, meaning they cannot be changed once written ("burned in").
These keys work much like the certificates used in HTTPS websites. There’s a root key in the chip’s ROM (read-only memory), and possibly other intermediate keys. The OEM (device manufacturer) creates its own signing keys, gets them approved by the silicon vendor (or uses a key already trusted by the ROM), and permanently stores a certificate on the chip.
To enable secure boot mode, the chipset performs a final irreversible operation often called “blowing the fuse.” Once this is done, the device can only boot images that are signed with trusted keys. An easier but still secure alternative is to use TEE (Trusted Execution Environment) capabilities. These allow you to store trusted keys in a safe, isolated area of the processor, protected by hardware. I’ll explain this mechanism in more detail in the last section of this article.
🧰 Boot Chain in Action.
When the device powers on:
The chipset starts the first trusted bootloader stored in ROM.
That bootloader verifies the next stage, usually another bootloader, up to the application bootloader (2.), which is responsible for loading the Linux kernel and Android.
Each stage checks the digital signature of the next to ensure nothing has been modified.
From here, it’s up to the OEM to make sure the kernel and system images are also verified. Different chipset vendors may offer different bootloaders, such as U-Boot or custom ones. OEMs can either use the secure boot support provided by the chip vendor or extend the bootloader themselves to add this functionality.
🧪 Verifying the System with dm-verity.
Once the application bootloader verifies the Linux kernel (using a cryptographic signature), it loads it and passes control along with information about the Android system image and metadata. This metadata includes secure hash data.
This is where dm-verity comes into play. It’s a feature in the Linux kernel that ensures parts of the system (like system, vendor, product, odm) have not been changed.
Here’s how it works:
Each file system block is cryptographically hashed.
These hashes form a structure called a hash tree, where all the data leads up to a single root hash.
The root hash is signed by the OEM during the build process and stored in a metadata image (vbmeta.img).
At runtime, when a block of data is read, the kernel uses dm-verity to recompute the hash and verify it matches the signed root hash.
This all happens transparently and efficiently, without slowing down the system significantly. If a mismatch is found, the system knows the file has been corrupted or tampered with and will return an I/O error effectively preventing access.
📌Important: dm-verity only works for read-only partitions that shouldn't change after the system is built, like system, system_ext, product, vendor, and odm.
🧬 The VBMeta struct.
The core data structure used in AVB (Android Verified Boot) is called the VBMeta struct. Think of it as a container that holds important verification data, like image hashes and other metadata and all of this information is cryptographically signed to make sure it hasn’t been tampered with. Inside this structure, you’ll find descriptors. These are used to describe things like:
Hashes of boot images
Metadata for hashtrees (used to verify large partitions like /system and /vendor)
Here’s a simple example of how it works:
The vbmeta partition stores the hash of the boot partition in a hash descriptor.
For larger partitions like system and vendor, the data is followed by a hashtree (a structure that allows quick verification of large files). The vbmeta partition then stores the root hash, salt, and offset of this hashtree in what’s called a hashtree descriptor.
Since the VBMeta struct itself is signed, the bootloader can check this signature using a trusted key (for example, key0), and verify that it hasn’t been changed. If the signature is valid, the bootloader knows it can trust the hashes inside and therefore trust the boot, system, and vendor partitions too.
⚙️ Enabling Verified Boot in AOSP Builds.
From Android 8 onward, AOSP includes a reference implementation of Verified Boot (also known as AVB - Android Verified Boot). To enable it during a build we need to add a line in the device’s makefile:
This tells the build system to:
Generate a metadata file called vbmeta.img.
Include cryptographic hashes for the boot image and system hash tree.
Set up the kernel to use dm-verity when it starts.
You can also customize the cryptographic algorithm and key path like this:
The public part of the key must be available to the bootloader so it can verify the image’s integrity. AOSP also provides:
Tools to extract and format the key.
A C library called libavb that OEMs can integrate into their bootloaders to handle verified boot properly.
💪 In the final section of this article, I’ll walk you through the detailed steps to implement Verified Boot on an Android device, starting from choosing the right System-on-Chip."
📌To sum it up.
We can wrap up this section by saying that Verified Boot makes sure of a few very important things:
Only trusted and unmodified code runs on your Android device.
The entire boot process is secure, starting from the hardware all the way up to Android.
Critical system partitions stay protected during runtime, thanks to dm-verity.
This is a powerful security feature that gives both users and developers confidence that the software running on their devices is safe and has not been tampered with.
👇 Here's a flow diagram of the boot process to summarize everything we've covered so far:
▶️ Let’s wrap up this section with a practical example to better understand everything we’ve covered so far: Imagine a user tries to modify the system partition to gain root access or install unauthorized software:
The modified system partition will no longer match the hash stored in vbmeta.
During boot, the bootloader checks the vbmeta using the embedded public key.
If the content was changed without updating the signatures, the verification will fail.
If the device has a locked bootloader, it won’t boot or it will show a red warning screen.
If the bootloader is unlocked, Android might still boot, but with a warning saying the system can’t be trusted.
In the next section, we’ll take a deep dive into the code, in fact, I’ll try to give a detailed technical explanation of dm-verity in Android, with direct references to AOSP and the kernel source code
🔍 Android dm-verity: A Deep Dive into the Implementation.
Let’s start by adding a few more details to what we’ve already said about dm-verity, and then we’ll dive into the kernel and AOSP code.
dm-verity Overview.
dm-verity is an important security feature in Android that helps ensure the integrity of the system’s disk blocks. It works quietly in the background, making sure no one has tampered with your device’s core files. At its core, dm-verity checks every block of data as it’s read from the disk. If everything looks good, the read goes through normally. If not, it triggers an I/O error, as if the block was physically damaged. This prevents any corrupted or tampered data from reaching the system. dm-verity relies on a precomputed hash tree, also known as a Merkle tree (3.). In this tree:
Leaf nodes hold the hashes of actual disk blocks.
Intermediate nodes store hashes of their children (in other words, “hashes of hashes”).
At the top sits the root hash, built from all lower-level hashes.
Even the smallest change in any block will change the root hash. So, to verify the entire tree, dm-verity only needs to check the root hash. When the system reads a block, dm-verity calculates the hash for that block and checks it by walking up the tree. Since reading from a disk already takes time, this extra verification step doesn’t add much delay. Plus, once a block is verified, it’s cached in memory for faster future reads.
For dm-verity to work, the partition must be mounted as read-only. That’s because even small metadata changes (like recording the last mount time) would break the verification process. While this might seem restrictive, it’s a perfect match for Android’s system partitions, which only change when the OS is updated. Any other change might mean corruption or an attempt to hack the device.
In Android’s security model, app data lives on a read-write partition, while the OS files are kept safe on a read-only system partition. dm-verity’s read-only requirement fits right in, helping protect the core of the system.
dm-verity was originally developed for Chrome OS to implement verified boot. It became part of the mainline Linux kernel in version 3.4. To use it, the kernel must have the CONFIG_DM_VERITY option enabled.
In Android, the RSA public key used for verification is stored in the boot partition under the name verity_key. This key checks the signature of the dm-verity mapping table, which contains: the location of the target device, the offset of the hash table, the root hash and the salt (we'll see what it's about soon). This mapping table and its signature are stored together in the verity metadata block, right after the last filesystem block of the target device.
A partition becomes verifiable when it has the verify flag in its fstab file. When Android’s filesystem manager sees this flag, it loads the verity metadata, verifies its signature using the verity_key, and, if everything is checked out, it parses the mapping table.
Finally, the filesystem manager passes this information to the Linux device-mapper, which sets up a virtual block device that uses dm-verity. This virtual device replaces the actual block device and is mounted at the mount point defined in fstab. All reads from this point on are automatically verified against the precomputed hash tree. If anyone tries to add or change files or even remount the partition as read-write the integrity check will fail, causing an I/O error.
Kernel Implementation.
The verification process relies on a cryptographic structure called a Merkle tree (or hash tree). Here's how it works in practice:
The entire partition is divided into fixed-size blocks (typically 4KB)
Each block gets cryptographically hashed (usually with SHA-256)
These hashes are then grouped and hashed again
The process repeats until reaching a single root hash
here's an overview of dm-verity structure:
This structure provides several key benefits: efficient verification (only need to check the chain up to the root), tamper-proof design (any change affects the root hash) and parallel verification capability.
The tree is stored alongside the actual data, with the root hash typically protected by Android Verified Boot (AVB).
At the heart of dm-verity lies its Linux kernel implementation. When initialized, it creates a virtual block device that intercepts all read operations. Here's how the kernel sets up this verification layer (snippet code from dm-verity-target.c 🔗):
This code creates the verification framework that will intercept all read operations. The dm_verity structure contains all the cryptographic parameters needed to verify blocks, including the root digest and salt value that make each device's verification unique. I remember you that Root digest is the top-level hash of the Merkle tree, calculated when the system image was built. It represents the full content of the verified partition while Salt is a random value used when computing hashes to make the resulting digests unique per device (without the salt, two devices with the same system image would have identical hashes instead with salt, even if the image is the same, the verification data is device-specific). (snippet code from dm_verity.h 🔗):
Device-Mapper Table Construction.
The actual device-mapper table is constructed here (snippet code from build_verity_tree.cpp 🔗):
Block Verification Process.
When your system reads a file from a protected partition, dm-verity performs an intricate verification dance (snippet code from dm-verity-target.c 🔗):
This verification happens transparently for every block read, creating continuous protection without user intervention. The code compares the computed hash of each block with its stored hash, and if they don't match, triggers the appropriate error handling.
Android's Integration: The fs_mgr Handshake.
Android's filesystem manager (fs_mgr) handles the crucial task of setting up dm-verity during early boot (snippet code from fs_mgr_verity.cpp 🔗):
This code shows how Android reads the verification parameters and establishes the protected device mapping. The fstab_entry contains crucial information like the location of the hash tree and the root digest.
The AVB Connection.
Then consider that dm-verity doesn't work alone, it's part of a larger security ecosystem that includes Android Verified Boot (AVB). Their integration is handled through careful coordination (snippet code from fs_mgr_avb.cpp 🔗, I simplified this snippet compared to the original code to avoid jumping between files and functions, but the meaning stays the same):
This code demonstrates how AVB's metadata provides the essential verification parameters that dm-verity needs to do its job. The root digest from AVB becomes the cryptographic anchor that the entire verification chain depends on.
Error Handling.
When dm-verity detects corruption, it doesn't panic it follows carefully designed error handling procedures (snippet code from fs_mgr.h 🔗):
The chosen mode depends on the security requirements of the partition being protected. Critical system partitions typically use more aggressive responses, while some vendor partitions might use logging mode for debugging.
📌 One last note
Let’s wrap up this section by noting that because dm-verity is a feature built into the kernel, its protection can only be trusted if the kernel itself is trusted. In Android, this means the boot partition (which includes the root filesystem’s RAM disk and the verification public key) must be verified. Checking the kernel or boot image is a crucial step that we have already covered in this article. It’s done by the device’s bootloader and relies on an unchangeable verification key stored in the hardware. In the next section, we’ll see how to enable verified boot.
📝 To sum it up.
The dm-verity implementation in Android represents a remarkable achievement in security engineering. From the kernel-level block verification to the Android framework integration, every component plays its part in creating a robust defense against data tampering. The code snippets we looked at show the careful design of efficient verification mechanisms, secure cryptographic practices, graceful error handling and seamless system-level integration. Together with AVB and other security features, dm-verity forms an essential layer in Android's defense-in-depth strategy, working silently in the background to keep your device's data intact and trustworthy.
In the next section, just like in every chapter of our deep, hidden and intricate world of the Android OS, we’ll roll up our sleeves and try to implement Verified Boot on a custom Android device platform.
🛠️ A Comprehensive Guide to Implementing Verified Boot on Android Devices.
Let's start with a good news. Android (AOSP) already gives you most of the building blocks for Verified Boot. You don’t have to start from scratch! Here’s what Android provides:
The full AVB (Android Verified Boot) implementation
Standard HAL interfaces to connect with hardware
Default security policies
Tools like avbtool to sign and verify images
But of course, there are still a few important things that you need to take care of:
🧩 Hardware integration: Making sure Verified Boot works smoothly with your specific device and SoC
🔐 Key management: Setting up and protecting the cryptographic keys that form your trust chain
📋 Policy decisions: Choosing what to do if the verification fails (e.g., block boot or show a warning)
Here's a comprehensive guide to implementing Verified Boot on your Android device, from hardware selection to AOSP configuration.
Phase 1: Hardware Selection.
Choosing the right hardware is your first critical decision. Not all chipsets are created equal when it comes to security features. You'll want a System-on-Chip (SoC) that offers:
A secure boot ROM that can't be modified: The Boot ROM is the immutable startup code embedded within the SoC (System-on-Chip). It cannot be modified because it is programmed directly into the chip and ensures that the device only boots verified, unaltered firmware. If the Boot ROM were modifiable, an attacker could compromise the entire boot process.
eFuses or OTP memory for storing security-critical data: eFuses (electronic fuses) and OTP (One-Time Programmable) memory are special memory areas within the chip that can be written once and never be overwritten afterward. These are used to store cryptographic keys or other security parameters needed to validate the OS and prevent tampering. For example, a device might use eFuses to store the Bootloader key, making it impossible to use unauthorized software.
Hardware crypto acceleration for efficient verification: Encryption is essential for maintaining system integrity, but it can be computationally intensive. Hardware acceleration provides dedicated units to perform cryptographic operations quickly and efficiently, improving verification speed without impacting overall device performance. This speeds up the validation of OS signatures and apps, ensuring they haven't been tampered with.
True TEE capabilities (like ARM TrustZone): The TEE (Trusted Execution Environment) is an isolated, secure processing area within the chip that executes critical operations without being accessible by the main OS. Secure World, which manages sensitive data like payment credentials, encryption keys, and certificate management. Normal World, where the OS and apps run, without direct access to protected data. Essentially, TEE minimizes the risk of malicious software compromising essential security operations.
One way to check if the SoC you're working with supports the security features mentioned above is by looking at the kernel configuration. You should check for the following options:
These settings enable the Trusted Execution Environment (TEE), which allows critical operations to run in a secure, isolated area of the processor. You can check if these options are present in the kernel by running
If the values are set to y, it means the kernel was compiled with support for these features.
Popular choices include Qualcomm's Snapdragon series, MediaTek chips, or NXP's i.MX processors. Each has its own implementation quirks, but the core principles remain similar.
Right now, I'm working on a device that will act as the brain of a line of cardiovascular training equipment. It’s based on the Rockchip RK3588, which provides strong support for essential security features like secure boot, TEE (Trusted Execution Environment), and hardware mechanisms for key protection.
Phase 2: Bootloader Modifications.
The bootloader serves as the first line of defense in your Verified Boot implementation. Here's where you'll make some important customizations in order to integrate Android Verified Boot (AVB). In the AOSP (Android Open Source Project), you won’t find the bootloader in the main source tree. That’s because bootloaders are hardware-specific, often developed and maintained by the SoC vendor or device manufacturer (OEM). However, many Android devices use a modified version of U-Boot as their bootloader. So, let’s talk about how to integrate Android Verified Boot (AVB) into U-Boot.
As we have already seen in the previous section AVB is a security framework designed to protect the Android boot process. Its main job is to make sure that critical system partitions like boot, system, and vendor haven’t been changed or tampered with.
AVB is implemented as a set of libraries and tools in the Android Open Source Project (AOSP). You can find the source code here: 📁 platform/external/avb/. In this folder, you’ll find the cryptographic verification code, tools to sign and verify boot images and configuration files that define which partitions should be verified. If you're using U-Boot as your bootloader (as many Android devices do), you'll need to integrate AVB into it manually. Here’s a basic overview of the steps:
1. Include AVB Code in U-Boot: Copy the needed AVB files from platform/external/avb/ in AOSP and adapt them for U-Boot. This will give your bootloader the ability to run AVB checks.
2. Modify the Boot Flow: You need to change the U-Boot initialization code so that it runs AVB verification before loading any system images. Here is a code snippet as an example:
3. Handle Verification Failures: If AVB detects that a partition has been modified or corrupted, U-Boot must respond properly. You can: ❌ Stop the boot process, ⚠️ Show a security warning, 🛑 Enter a safe or recovery mode (if available).
4. Set Up Signing Keys: AVB relies on public keys to verify that each partition is correctly signed. So you’ll need to set up your key management: Add the public key to the bootloader and make sure the system images are signed with the matching private key.
Phase 3: AOSP Configuration: Telling Android About Your Security
When building Android from source, you'll need to properly configure your device's build system to support Verified Boot. This involves several key files:
BoardConfig.mk: This is where you declare your Verified Boot capabilities and algorithms:
device.mk: Here you'll inherit standard AVB policies:
BoardConfig.mk and device.mk in the AOSP project are located in your specific device directory. The AOSP already includes most of the Verified Boot infrastructure, your job is to properly enable and configure it for your specific hardware.
Phase 4: Key Management.
Proper key management makes or breaks your Verified Boot implementation. You'll need to establish a hierarchy of keys. There are at least two main keys to manage:
The Root keys that is the ultimate source of trust, they are used to create an authentication chain, allowing the verification of other keys (signing keys and device-specific keys), ideally stored in hardware.
The Signing keys that is used to sign critical boot components, such as the bootloader, kernel, and operating system..
The golden rule is never expose your private keys, they should live in secure environments so let's see how they are managed.
As we have already said if your SoC has a TEE (Trusted Execution Environment) or a OTP(One-Time Programmable) memory, you can write and store a cryptographic key in hardware securely, preventing unauthorized access. Here are the main strategies:
1. Using eFuses or OTP Memory: Some SoCs include eFuses (electronic fuses) or OTP (One-Time Programmable) memory, allowing you to write a key once and make it immutable. This technique is useful for storing a Root Key, essential for Verified Boot. Use OEM-specific tools for programming eFuses and write the key.
2. Storing in TEE (TrustZone): If your SoC supports ARM TrustZone, you can store the key in a protected area of the processor, accessible only by code running in the Secure World (We have covered this topic in the phase one of this section). Use Android Keystore API to generate and store the key securely. Here’s a Java code snippet to generate an RSA key and store it in the TEE using the Android Keystore:
Here is a short explanation of this code: Uses the Android Keystore to generate a secure RSA key, configures the key to be stored in TEE (if the device supports StrongBox), enables user authentication protection ensuring that the key can only be accessed by an authenticated user and finally retrieves the private key from the Keystore when needed.
Phase 5: TEE Integration.
The Trusted Execution Environment acts as your secure co-processor, handling sensitive operations in isolation. Integration involves:
Selecting a TEE implementation (OP-TEE, Trusty, or vendor-specific): choose which TEE technology to integrate. The most common options are:
OP-TEE (Open Portable TEE): An open-source implementation based on ARM TrustZone, provides APIs to handle secure operations like encryption and authentication.
Trusty (Google's TEE): Google’s TEE for Android (used for example in Pixel devices). Provides functionality to handle cryptographic operations within the Secure World. Integrates directly into the Android security stack.
Vendor-specific TEE: Some manufacturers (Qualcomm, MediaTek, Samsung) implement proprietary TEEs with advanced security features. These solutions may offer hardware optimizations not found in open-source implementations.
Configuring the Linux kernel to support your chosen TEE. To make the TEE function properly, you must enable support in the Linux kernel. Important configurations include:
If these configurations are present and set to y, the kernel supports TEE and can communicate with the Secure World. Without these configurations, the system cannot utilize TEE-based security.
Implementing Keymaster HAL (Hardware Abstraction Layer) is the Android module responsible for managing cryptographic keys within the TEE. It manages the generation and usage of cryptographic keys inside the Secure World and it is used by Verified Boot to ensure that bootloader and system images are properly signed. Implementation in AOSP Keymaster source code is located at: 📂 hardware/interfaces/keymaster/ 📂 system/keymaster/
Here is an example of a Keymaster call to generate an RSA key:
This code securely generates and stores a cryptographic key in the TEE, ensuring that it is used in a protected environment.
Phase 6: Building and Signing.
This phase in Verified Boot implementation is crucial for ensuring that the Android system is authenticated and protected before booting. Although the build system automatically handles signing thanks to prior configurations, it’s important to understand what happens behind the scenes. Let’s break down the three key steps in detail.
1. vbmeta image creation: The vbmeta.img file is the root of the verification chain in Android Verified Boot (AVB). It stores verification metadata for the device’s critical partitions. Defines the AVB security level (such as AVB_MODE_ENFORCED, which prevents unauthorized modifications). Uses digital signatures to ensure the device only loads verified firmware. During the system build process, the avbtool utility is used to create vbmeta.img:
The bootloader loads vbmeta.img to verify the integrity of signed partitions.
2. Partition signing: Each critical partition gets its own signature. After generating vbmeta.img, each critical partition containing essential system data must be individually signed (boot.img contains the Android kernel and ramdisk, system.img includes the operating system and core files, vendor.img contains vendor-specific drivers and modules. Signing a partition using avbtool:
Each signed image is verified through vbmeta.img, preventing unauthorized modifications.
3. Footer addition: The final step is attaching the AVB footer to each image, embedding the verification metadata. The footer include a digital signature for the partition and a cryptographic hash ensuring data integrity. Checking if an image has the AVB footer:
If the AVB footer is present, Verified Boot can authenticate the partition’s integrity.
Phase 7: Testing and Validation: Proving Your Implementation
This phase is crucial for ensuring that Verified Boot is effectively implemented and secure. Even if everything was set up correctly in previous phases, thorough testing is necessary to confirm that the system works as intended and can withstand attacks or failures. Let’s break down the four key aspects of this phase in detail.
1. Verifying Signed Images (Verify the Verification): The objective is to ensure that all critical system images are correctly signed and verified. The avbtool utility is the official tool for managing and verifying Android Verified Boot (AVB). You can check whether images are signed correctly using:
If verification succeeds, the bootloader can authenticate the image and boot it safely. If verification fails, check whether the signing key is valid and if the AVB footer is present.
2. Testing Failure Modes (Test Failure Modes): The objective is to simulate scenarios where verification fails and observe the system’s response. Potential Failure Scenarios: Corrupted or tampered image (should prevent the system from booting), Unsigned partition (the bootloader should display an error message), Invalid key (should block execution or require system recovery).
Here are some test cases: Manually modify a system image (system.img) and attempt to boot the device. Remove the AVB footer and verify whether the device refuses to boot. Set the bootloader to AVB_MODE_ENFORCED and confirm that it rejects unauthorized firmware. These tests ensure that the system cannot boot unverified images.
3. Validating Secure Operations in TEE (Validate TEE Operations): The objective is to ensure that critical security functions within the Trusted Execution Environment (TEE) are working properly.
Here are some tests we can do inside TEE: Cryptographic key management (keys stored in the TEE should be accessible only to authorized processes). Protection against runtime attacks (simulating unauthorized access attempts to TEE operations). You can use Android Keystore API to test key usage within the TEE:
If unauthorized processes are blocked from accessing the key, the TEE implementation is secure.
To summarize this phase we can say that testing image verification with avbtool ensures that the system is properly authenticated, simulating failure modes verifies how the system responds to attacks and malfunctions and validating the TEE ensures that critical security operations are protected.
🧠 Let’s wrap up this section with a Final Thoughts.
Setting up Verified Boot isn’t exactly plug-and-play, it takes time and effort. But it’s worth it. A good Verified Boot setup greatly improves the overall security of your device.
The good part is: Android handles most of the hard work for you. Your main focus should be:
Connecting everything properly
Managing your keys securely
Testing every part of the boot process carefully
And keep in mind: security isn’t a one-time job. Even after you get Verified Boot working, your job isn’t done. You’ll need to stay updated on new security threats and follow Android security updates.
This is where our article ends, for now.
In the next episode, we’ll keep exploring the Android Security Architecture and meet the next key player in our story: Agent SELinux. Stay tuned! 🕵️♂️
I remind you my newsletter "Sw Design & Clean Architecture": https://lnkd.in/eUzYBuEX where you can find my previous articles and where you can register, if you have not already done, so you will be notified when I publish new articles.
Thanks for reading my article, and I hope you have found the topic useful,
Feel free to leave any feedback.
Your feedback is very appreciated.
Thanks again.
Stefano
References:
1. S.Santilli: Inside the Android Fortress: Disarming Runtime Permissions through low-level AOSP modifications.
2. S.Santilli: A Deep Dive into the Android Boot Process: Step-by-Step Breakdown.
3. Nikolay Elenkov, “Android Security Internals” No Starch Press (October 2014).
4. G.Meike, L.Schiefer, “Inside the Android OS” Addison Wesley (August 2021).