Analyzing a dump of a Samsung Galaxy S7 /data partition with Samsung encryption

So you took a backup of your Samsung Galaxy S7’s /data partition as described in the previous post. Let’s have a look at its encryption!

Seeing that Android is Linux-based, it seemed manifest to use the dm-crypt target of Linux’s Device Mapper framework for the Full Disk Encryption feature. I won’t get into details on the keys here, others do a far better job in describing that. Common sense tells us that at boot the phone must somehow know how to mount the encrypted partition. The structure used to store this information is called a crypto footer and is defined in the Android sources. According to László Tóth and Ferenc Spala, the footer can be found in a file /efs/metdata or at the end of the encrypted partition itself (thus the “footer”), usually within the last 16KB. It starts with the magic number 0xD0B5B1C4.

So I took the dump of the /data partition on my Galaxy S7 running Android 8.0 and tried this out. My dump had a size of 26843545600 bytes, or 25GB. Subtracting 16KB gave me the offset for the dd command to extract this part and examine it:

$ dd if=s7-data of=footer skip=26843529216 bs=1
$ head -c4 footer | hexdump -C
00000000 c5 b1 b5 d0 |....|
00000004

The magic was defined a little-endian 32-bit value — almost the value we can find at the start of the footer (shown above), except the value on-disk is increased by one. So yes, this looks like the right structure and the S7 uses the footer method, not /efs/metadata (that file does not exist then, too), but with a different, Samsung-specific magic number.

I then tried to decode the remainder of the structure anyway as follows:

00000000  c5 b1 b5 d0 01 00 03 00  90 09 00 00 00 00 00 08 |................|
00000010  20 00 00 00 03 00 00 00  e0 ff 1f 03 00 00 00 00 | ...............|
00000020  00 00 00 00 61 65 73 2d  78 74 73 2d 66 6d 70 00 |....aes-xts-fmp.|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000060  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000070  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000080  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000090  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
000000a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
000000b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
000000c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
000000d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
000000e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
000000f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000100  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000110  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000120  00 00 00 00 00 00 00 00  00 d0 ff 3f 06 00 00 00 |...........?....|
00000130  00 e0 ff 3f 06 00 00 00  00 0c 00 00 02 0f 03 01 |...?............|
00000140  e0 ff 1f 03 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000150  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000160  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
[...]
000008d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
000008e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
000008f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000900  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000910  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000920  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000930  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
[...]

struct crypt_mnt_ftr (v1.3)
Offset Type Name Comment Value
0x000 __le32 magic Magic value to identify the structure = 0xD0B5B1C4 0xD0B5B1C5
0x004 __le16 major_version CURRENT_MAJOR_VERSION = 1 1
0x006 __le16 minor_version CURRENT_MINOR_VERSION = 3 3
0x008 __le32 ftr_size In bytes, not including key following 0x0990 = 2448
0x00C __le32 flags
0x01 CRYPT_MNT_KEY_UNENCRYPTED The key for the partition is not encrypted.
0x02 CRYPT_ENCRYPTION_IN_PROGRESS Encryption partially completed, encrypted_upto valid
0x04 CRYPT_INCONSISTENT_STATE Set when starting encryption, clear when exit cleanly, either through success or correctly marked partial encryption
0x08 CRYPT_DATA_CORRUPT Set when encryption is fine, but the underlying volume is corrupt
0x10 CRYPT_FORCE_ENCRYPTION Set when it is time to encrypt this volume on boot. Everything in this structure is set up correctly as though device is encrypted except that the master key is encrypted with the default password.
0x20 CRYPT_FORCE_COMPLETE Set when the above encryption cycle is complete. On next cryptkeeper entry, match the password. If it matches fix the master key and remove this flag.
0x08000000
0x010 __le32 keysize In bytes 0x20 = 32
0x014 __le32 crypt_type How master_key is encrypted.

0 CRYPT_TYPE_PASSWORD Master_key is encrypted with a password. Must be zero to be compatible with pre-L devices where type is always password.
1 CRYPT_TYPE_DEFAULT Master_key is encrypted with default password
2 CRYPT_TYPE_PATTERN Master_key is encrypted with a pattern
3 CRYPT_TYPE_PIN Master_key is encrypted with a pin
3
0x018 __le64 fs_size Size of the encrypted fs, in 512 byte sectors 0x031fffe0 = 52428768
0x020 __le32 failed_decrypt_count Count of # of failed attempts to decrypt and mount, set to 0 on successful mount 0
0x024 unsigned char[MAX_CRYPTO_TYPE_NAME_LEN = 64] crypto_type_name The type of encryption needed to decrypt this partition, null terminated. aes-xts-fmp
0x064 __le32 spare2 Ignored 0x0
0x068 unsigned char[MAX_KEY_LEN = 48] master_key The encrypted key for decrypting the filesystem None
0x098 unsigned char[SALT_LEN = 16] salt The salt used for this encryption None
0x0A8 __le64[2] persist_data_offset Absolute offset to both copies of crypt_persist_data on device with that info, either the footer of the real_blkdevice or the metadata partition. 0
0x0B8 __le32 persist_data_size The number of bytes allocated to each copy of the persistent data table 0
0xBC __le8 kdf_type The key derivation function used.

1 KDF_PBKDF2
2 KDF_SCRYPT
5 KDF_SCRYPT_KEYMASTER
0
0x0BD __le8 N_factor scrypt parameter (1 << N) 0
0x0BE __le8 r_factor scrypt parameter (1 << r) 0
0x0BF __le8 p_factor scrypt parameter (1 << p) 0
0x0C0 __le64 encrypted_upto If we are in state CRYPT_ENCRYPTION_IN_PROGRESS and we have to stop (e.g. power low) this is the last encrypted 512 byte sector. 0
0x0C8 __le8[SHA256_DIGEST_LENGTH = 32] hash_first_block When CRYPT_ENCRYPTION_IN_PROGRESS set, hash of first block, used to validate before continuing 0
0x0E8 __le8[KEYMASTER_BLOB_SIZE = 2048] keymaster_blob key_master key, used to sign the derived key which is then used to generate the intermediate key. This key should be used for no other purposes! We use this key to sign unpadded data, which is acceptable but only if the key is not reused elsewhere. Garbage
0x8E8 __le32 keymaster_blob_size 0
0x8EC unsigned char[SCRYPT_LEN = 32] scrypted_intermediate_key Store scrypt of salted intermediate key. When decryption fails, we can check if this matches, and if it does, we know that the problem is with the drive, and there is no point in asking the user for more passwords.

Note that if any part of this structure is corrupt, this will not match and we will continue to believe the user entered the wrong password. In that case the only solution is for the user to enter a password enough times to force a wipe.

Note also that there is no need to worry about migration. If this data is wrong, we simply won’t recognise a right password, and will continue to prompt. On the first password change, this value will be populated and
then we will be OK.

0
0x90C unsigned char[SHA256_DIGEST_LENGTH = 32] sha256 sha of this structure with this element set to zero. Used when encrypting on reboot to validate structure before doing something fatal. 0

Quite obviously this doesn’t make sense but this is where I’m stuck for now…


Update 1 (January 29th, 2018): In Android 4.4 the attackable PBKDF2 key derivation algorithm was amended by scrypt. To quote “Android Security Internals” by Nikolay Elenkov, page 261:

Scrypt can be tuned by specifying the variable parameters N, r and p, which influence the required CPU resources, memory amount, and parallelization cost, respectively. The values used in Android by default are N = 32768 (215), r = 8, and p = 2. They can be changed by setting the value of the ro.crypto.scrypt_params system property using the N_factor:r_factor:p_factor format; for example, 15:3:1 (the default). The value of each parameter is computed by raising 2 to the power of the respective factor.

This suggests looking for the hex byte sequence 02 0f 03 01 (02: KDF_SCRYPT, 0f: log2(N = 32768) = 15, 03: log2(r = 8) = 3, 01: log2(p = 2) = 1). And sure enough we can find this sequence, albeit on a different offset than in the standard Android crypto footer, which allows us to deduce other fields as well:

00000000  c5 b1 b5 d0 01 00 03 00  90 09 00 00 00 00 00 08 |................|
00000010  20 00 00 00 03 00 00 00  e0 ff 1f 03 00 00 00 00 | ...............|
00000020  00 00 00 00 61 65 73 2d  78 74 73 2d 66 6d 70 00 |....aes-xts-fmp.|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000060  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000070  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000080  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000090  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
000000a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
000000b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
000000c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
000000d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
000000e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
000000f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000100  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000110  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000120  00 00 00 00 00 00 00 00  00 d0 ff 3f 06 00 00 00 |...........?....|
00000130  00 e0 ff 3f 06 00 00 00  00 0c 00 00 02 0f 03 01 |...?............|
00000140  e0 ff 1f 03 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000150  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
00000160  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|

struct crypt_mnt_ftr (v1.3)
Offset Type Name Comment Value
0x128 __le64[2] persist_data_offset Absolute offset to both copies of crypt_persist_data on device with that info, either the footer of the real_blkdevice or the metadata partition. Copy 1: 0x063fffd000 = 26843533312
Copy 2: 0x063fffe000 = 26843537408
0x138 __le32 persist_data_size The number of bytes allocated to each copy of the persistent data table 0x0c00 = 3072
0x13C __le8 kdf_type The key derivation function used.

1 KDF_PBKDF2
2 KDF_SCRYPT
5 KDF_SCRYPT_KEYMASTER
2 (KDF_SCRYPT)
0x13D __le8 N_factor scrypt parameter (1 << N) 0x0f = 15
0x13E __le8 r_factor scrypt parameter (1 << r) 3
0x13F __le8 p_factor scrypt parameter (1 << p) 1
0x140 __le64 encrypted_upto If we are in state CRYPT_ENCRYPTION_IN_PROGRESS and we have to stop (e.g. power low) this is the last encrypted 512 byte sector. 0x031fffe0 = 52428768. Multiplied with sector size 512 this gives 26843529216, which is the first sector behind the filesystem (we used it as skip= parameter above).
0x148 __le8[SHA256_DIGEST_LENGTH = 32] hash_first_block When CRYPT_ENCRYPTION_IN_PROGRESS set, hash of first block, used to validate before continuing 0

This still leaves me wondering why the offsets for master_key, salt etc. don’t match. Whereas them being zero suggests to me that some hardware crypto stuff is in the works here and the fields themselves are no longer really used.

In any case, should you have been wondering, whether it’s possible to offline decrypt the /data partition, the answer is a clear no! As the diagram at the end of this blog post by renowned Android security expert shows, Android versions since Lollipop use a hardware-specific key to derive the final DEK. Without this key, which can’t be extracted from the Android phone, it is impossible to derive the DEK. This also means that a low-level partition-level backup is of no use! If the phone dies, all your data will be lost.