2022 APFS Advent Challenge Day 18 - Decryption

Monday, December 26, 2022

Now that we know how to parse the File System Tree, Analyze Keybags, and Unwrap Decryption Keys, it’s time to put it all together and learn how to decrypt file system metadata and file data on encrypted volumes in APFS.


All encryption in APFS is based on the XTS-AES-128 cipher, which uses a 256-bit key and a 64-bit “tweak” value. This tweak value is position dependent. It allows the same plaintext to be encrypted and stored in different locations on disk and have drastically different ciphertext while using the same AES key. Every 512 bytes of encrypted data uses a tweak based on the container offset of the block’s initial storage.

Knowledge of the AES key alone is not always enough for successful decryption. If the encrypted block is ever relocated on disk, the data is not guaranteed to be re-encrypted with a new tweak. In these cases, the tweak can not be inferred based on the block’s on-disk location, so we must learn the original tweak value used for encryption.

Identifing Encrypted Blocks

There are primarily two sets of data protected with the APFS Volume Encryption Key: File System Tree Nodes and File Extents. As we’ve discussed, File System Tree Nodes store the File System Records that contain the file system’s metadata, and File Extents contain the bulk of the data stored in a file’s Data Streams.

Encrypted FS-Tree Nodes

A volume’s Object Map is never encrypted, but its referenced virtual objects may be, as is the case with FS-Tree Node on encrypted volumes.

Let’s revisit the value half of an Object Map entry.

typedef struct omap_val {
  uint32_t ov_flags; // 0x00
  uint32_t ov_size;  // 0x04
  paddr_t ov_paddr;  // 0x08
} omap_val_t;        // 0x10

If the ov_flags bit-field member has the OMAP_VAL_ENCRYPTED flag set, then the virtual object located at ov_paddr is encrypted. These objects are never related without being re-encrypted, so the tweak of the first 512 bytes of data can be determined by the physical location of the data using the following logic, with the following tweak values incremented for each subsequent 512 bytes of data:

uint64_t tweak0 = (ov_paddr * block_size) / 512;

Encrypted Extents

Extent data can be relocated on disk and is not guaranteed to be re-encrypted. Due to this, the initial tweak value is stored in the crypto_id field of the j_file_extent_val_t file system record:

typedef struct j_file_extent_val {
  uint64_t len_and_flags;  // 0x00
  uint64_t phys_block_num; // 0x08
  uint64_t crypto_id;      // 0x10
} j_file_extent_val_t;     // 0x18


We’ve now discussed all of the information needed to access data on software-encrypted APFS volumes. This decryption requires the knowledge of the password of any user on the system or one of the various recovery keys. While APFS hardware encryption works in largely the same manner, the encryption also depends on keys that are stored within the specific security chip on a given system. There are currently no known methods of extracting these chip-specific keys; therefore, the data on hardware-encrypted devices must be decrypted at acquisition time on the device itself. The only software that I am aware of that is capable of this is Cellebrite’s Digital Collector.

Full disclosure: I currently work for Cellebrite and helped develop these capabilities. I do not directly profit from the sales of Digital Collector but felt it appropriate to disclose my association when linking to a commercial product. I am not trying to sell you anything. Unfortunately, I am also not at liberty to discuss the methodology used to facilitate this decryption.

This post is part of my 2022 APFS Advent Challenge

Every weekday in the month of December, I will attempt to post a blog about APFS internals. For each day that I miss a post, I will donate $100 to support humanitarian aid for the Ukrainian people. If you find value in this series, and would like to support this effort, please consider donating to the GoFundMe. Slava Ukraini! 🇺🇦

Find an issue or technical inaccuracy in this post? Please file an issue so that it may be corrected.