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 |
| 0x08000000 | ||||||||||||||||||
0x010 | __le32 | keysize | In bytes | 0x20 = 32 | ||||||||||||||||||
0x014 | __le32 | crypt_type | How master_key is encrypted.
| 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.
| 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 | 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.
| 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.