Gilad is the maintainer of the ARM TrustZone CryptoCell Linux device driver.
Smart devices are used for everything, so we need to be able to trust them. However, we also want a frictionless user experience and be able to do anything with it. This is guaranteed to fail, so we need a second line of defence, a trusted way of failing. If someone gets hold of our device, we don’t want them to have access to all our secrets, to get access to additional resources, and we want to know about it and be able to get them out again. We want trusted boot: reboot the device and it is safe again.
All the components are there, we have to make them fit together.
Secure boot (Android style, but others are similar): chain of trust through the boot process, each component verifies the next one. ROM uses a public key in e.g. eFuses to verify bootloader. Bootloader verifies kernel and boot fs. OS verifies the full rootfs. Root key can also be in flash with just a hash in eFuses, or it can be a certificate chain with just hte hash of the root in eFuses.
Checking rootfs is done with DM-verity. It prevents a persistent rootkit: if the persistent storage is changed, we will know. DM-verity adds hashes and signatures to a readonly filesystem using device-mapper. Check is done every time we access the filesystem, not at boot. It uses a Merkle tree of hashes of blocks to arrive at a root hash that can be verified through a signature. The Merkle tree is stored on the device, so we need to verify log4096(device size) hashes. Cfr. figure in the slides.
This works only for readonly devices: when a block changes, the entire Merkle tree changes. For read-write data, the simplest is using full-disk encryption (dm-crypt) which implicitly does authentication. dm-crypt is a device-mapper layer between the actual filesystem (e.g. ext4) and the block device (e.g. eMMC) so neither of these knows about the encryption. This uses a single key for everything accessing the device, and the key is kept in memory all the time. The key is password-protected.
Problem with whole-disk encryption: multiple users, not possible to avoid encryption for some use cases. For example, alarm clock app is in encrypted storage, if the device reboots during the night, you have to give the password before the alarm clock can start running…. fscrypt solves this by pushing encryption into the fs layer, which allows different or no encryption keys for different directories and files. So e.g. the alarm clock app may be encrypted with a key that is stored in the rootfs, while the sensitive information is encrypted with a user-provided password. Limitation of fscypt: doesn’t hide all metadata, e.g. file size is not encrypted. Multiple keys can be loaded separately into the kernel. When the key is available in the kernel, you can see the file. When the key is not loaded, you can see there is a file but not its name or content.
The problem is the key: it has to be put in the kernel and stay there, so it is vulnerable it the kernel is compromised. Solution is some trusted execution environment, e.g. TrustZone in ARM. TrustZone is a hypervisor mode (called TEE, Trusted Execution Environment) that has access to memory that the normal OS has no access to. The OS then asks the TEE to store the key in memory that is not accessible to the kernel. It is never possible to get it out again; to do encryption, the kernel asks the TEE to put the key in a hardware crypto engine.
Instead of a TEE, you can also use a Trusted Platform Module (TPM) discrete from the CPU. Keys are directly stored in there and never go to flash; they are even generated in the TPM so they really never ever go to memory. But of course the TPM can still have bugs that can be exploited. The TPM can also do attestation: give access to certificates only if a certain set of hashes (of the HW and SW state) is provided in a certain order. This is done with Integrity Measurement Architecture (IMA) subsystem in Linux. Attestation is a way to check a sequence of hashes without needing to store all the hashes.