LUKS Emergency Erase
To solve your problem, the best way to wipe a LUKS header is to use the luksErase
command. To quote the man page:'
luksErase <device>
Erase all keyslots and make the LUKS container permanently inaccessible.
You do not need to provide any password for this operation.
WARNING: This operation is irreversible.
LUKS Header Start Byte
But to answer your question, a LUKS device's header necessarily starts at byte zero.
This is distinct from a LUKS container, which could start at a non-zero offset. In any case, the LUKS container's start byte would be found by searching for the byte sequence 0x4c55 0x4b53 0xbabe
, where (encoded as ASCII):
0x4c = 'L'
0x55 = 'U'
0x4b = 'K'
0x53 = 'S'
LUKS Header End Byte
The end byte is trickier, and to get it we must parse the LUKS headers.
First we parse the LUKS binary header to determine the version. Currently, there are only two versions of LUKS. LUKS1 was released in 2014 and LUKS2 was released in 2018. The encoding for both is very different, but the first 8 bytes of the LUKS header are encoded the same:
+-------+--------+-------------+
| Start | Length | Field Name |
+-------+--------+-------------+
| 0 | 6 | magic |
| 6 | 2 | version |
+-------+--------+-------------+
LUKS1
For LUKS1, the easiest way to determine where the LUKS header ends is to determine where the encrypted data (aka the "payload bulk data") begins, since the encrypted data begins immediately after the LUKS header ends.
To get the start offset of the payload, we need to get the value of payload-offset
. From "Figure 1: PHDR layout" of LUKS1 On-Disk Format Specification, the payload-offset
field starts in the LUKS header at an offset of 104 bytes and is 4 bytes long.
For example, the following hex dump of a LUKS container shows the version is 00 01
(LUKS1) and the payload-offset
is 0x1000
(hex) = 4096
(decimal).
root@disp4117:~# hexdump -Cs 6 -n 2 luksVol1
00000006 00 01 |..|
00000008
root@disp4117:~# hexdump -Cs 104 -n 4 luksVol1
00000068 00 00 10 00 |....|
0000006c
root@disp4117:~#
The payload-offset
just defines the number of sectors (in 512 bytes sectors) after the LUKS header start where the encrypted data begins (note that the location where the encrypted data begins is exactly the same as the location where the LUKS header ends), so in order to get the bytes where the payload begins, we have to multiply payload-offset
by 512. 4096 * 512
= 2097152
Therefore, for the example above, the LUKS1 header ends at byte 2097152.
LUKS2
For LUKS2, there is no payload-offset
field in the binary header. Rather than iterating through all of the keyslot objects, the closet equivalent to the payload-offset
is the offset
attribute for the first segment
object defined in the JSON metadata (this is new to LUKS2).
But before we can parse the JSON object, we have to find it! In LUKS2, there's six distinct header areas:
\/ primary binary header alignment padding \/
+----+----------+----+----------+-------------------+----+
| /\ | 1st JSON | \/ | 2nd JSON | Keyslots area | /\ |
+----+----------+----+----------+-------------------+----+
/\ secondary binary header
As you can see above, LUKS2 actually has two copies of the plaintext metadata. It's stored in the "primary binary header" and the "1st JSON" area (the second copy is called the "secondary binary header" and "2nd JSON" area, respectfully). In most cases the data in the "primary binary header" exactly matches the "secondary binary header" and the data in the "1st JSON" area exactly matches the "2nd JSON" area. The data is stored twice to aid in recovery and protect against corruption.
The "1st JSON" area always starts at byte 4096 (immediately after the "primary binary header" area), but its length is variable. The length can be determined by looking at the binary header's hdr_size
. From "Figure 2: LUKS2 binary header on-disk structure" in LUKS2 On-Disk Format Specification, hdr_size
is 8 bytes long and starts at byte 8 in the binary header.
For example, the following hex dump of a LUKS container shows the version is 00 02
(LUKS2) and the hdr_size
is 0x4000
(hex) = 16384
(decimal).
root@disp4117:~# hexdump -Cs 6 -n 2 luksVol2
00000006 00 02 |..|
00000008
root@disp4117:~# hexdump -Cs 8 -n 8 luksVol2
00000008 00 00 00 00 00 00 40 00 |......@.|
00000010
root@disp4117:~#
From the above hex dump, we clearly see the hdr_size
field has a value of 0x4000
(hex) = 16384
(decimal). But the hdr_size
field defines the size of the the "primary binary header" plus the "1st JSON" area. So the length of the "1st JSON" area is the hdr_size
value minus the "primary binary header" length. In this case, that's 16384 - 4096
= 122881
. Depending on the length of the LUKS2 container's metadata stored in the JSON object, the JSON area could be up to 4194304 - 4096
= 4190208
bytes long.
So here's a hexdump
of the "1st JSON" area, starting at byte offset 4096
with length 122881
:
root@disp4117:~# hexdump -Cs 4096 -n 12288 luksVol2
00001000 7b 22 6b 65 79 73 6c 6f 74 73 22 3a 7b 22 30 22 |{"keyslots":{"0"|
00001010 3a 7b 22 74 79 70 65 22 3a 22 6c 75 6b 73 32 22 |:{"type":"luks2"|
00001020 2c 22 6b 65 79 5f 73 69 7a 65 22 3a 36 34 2c 22 |,"key_size":64,"|
00001030 61 66 22 3a 7b 22 74 79 70 65 22 3a 22 6c 75 6b |af":{"type":"luk|
00001040 73 31 22 2c 22 73 74 72 69 70 65 73 22 3a 34 30 |s1","stripes":40|
00001050 30 30 2c 22 68 61 73 68 22 3a 22 73 68 61 32 35 |00,"hash":"sha25|
00001060 36 22 7d 2c 22 61 72 65 61 22 3a 7b 22 74 79 70 |6"},"area":{"typ|
00001070 65 22 3a 22 72 61 77 22 2c 22 6f 66 66 73 65 74 |e":"raw","offset|
00001080 22 3a 22 33 32 37 36 38 22 2c 22 73 69 7a 65 22 |":"32768","size"|
00001090 3a 22 32 35 38 30 34 38 22 2c 22 65 6e 63 72 79 |:"258048","encry|
000010a0 70 74 69 6f 6e 22 3a 22 61 65 73 2d 78 74 73 2d |ption":"aes-xts-|
000010b0 70 6c 61 69 6e 36 34 22 2c 22 6b 65 79 5f 73 69 |plain64","key_si|
000010c0 7a 65 22 3a 36 34 7d 2c 22 6b 64 66 22 3a 7b 22 |ze":64},"kdf":{"|
000010d0 74 79 70 65 22 3a 22 61 72 67 6f 6e 32 69 22 2c |type":"argon2i",|
000010e0 22 74 69 6d 65 22 3a 34 2c 22 6d 65 6d 6f 72 79 |"time":4,"memory|
000010f0 22 3a 32 37 34 35 33 30 2c 22 63 70 75 73 22 3a |":274530,"cpus":|
00001100 32 2c 22 73 61 6c 74 22 3a 22 71 4a 6e 79 2b 4a |2,"salt":"qJny+J|
00001110 5c 2f 6f 35 71 77 57 77 35 78 2b 57 31 30 7a 47 |\/o5qwWw5x+W10zG|
00001120 59 54 6f 64 44 64 57 6f 39 6e 74 5c 2f 6c 67 49 |YTodDdWo9nt\/lgI|
00001130 41 61 61 6f 78 5c 2f 45 3d 22 7d 7d 7d 2c 22 74 |Aaaox\/E="}}},"t|
00001140 6f 6b 65 6e 73 22 3a 7b 7d 2c 22 73 65 67 6d 65 |okens":{},"segme|
00001150 6e 74 73 22 3a 7b 22 30 22 3a 7b 22 74 79 70 65 |nts":{"0":{"type|
00001160 22 3a 22 63 72 79 70 74 22 2c 22 6f 66 66 73 65 |":"crypt","offse|
00001170 74 22 3a 22 31 36 37 37 37 32 31 36 22 2c 22 69 |t":"16777216","i|
00001180 76 5f 74 77 65 61 6b 22 3a 22 30 22 2c 22 73 69 |v_tweak":"0","si|
00001190 7a 65 22 3a 22 64 79 6e 61 6d 69 63 22 2c 22 65 |ze":"dynamic","e|
000011a0 6e 63 72 79 70 74 69 6f 6e 22 3a 22 61 65 73 2d |ncryption":"aes-|
000011b0 78 74 73 2d 70 6c 61 69 6e 36 34 22 2c 22 73 65 |xts-plain64","se|
000011c0 63 74 6f 72 5f 73 69 7a 65 22 3a 35 31 32 7d 7d |ctor_size":512}}|
000011d0 2c 22 64 69 67 65 73 74 73 22 3a 7b 22 30 22 3a |,"digests":{"0":|
000011e0 7b 22 74 79 70 65 22 3a 22 70 62 6b 64 66 32 22 |{"type":"pbkdf2"|
000011f0 2c 22 6b 65 79 73 6c 6f 74 73 22 3a 5b 22 30 22 |,"keyslots":["0"|
00001200 5d 2c 22 73 65 67 6d 65 6e 74 73 22 3a 5b 22 30 |],"segments":["0|
00001210 22 5d 2c 22 68 61 73 68 22 3a 22 73 68 61 32 35 |"],"hash":"sha25|
00001220 36 22 2c 22 69 74 65 72 61 74 69 6f 6e 73 22 3a |6","iterations":|
00001230 36 31 39 34 33 2c 22 73 61 6c 74 22 3a 22 46 69 |61943,"salt":"Fi|
00001240 4c 67 31 35 56 5c 2f 55 56 4b 47 72 72 4e 39 4f |Lg15V\/UVKGrrN9O|
00001250 52 2b 5c 2f 69 59 46 51 70 38 38 59 44 77 50 4c |R+\/iYFQp88YDwPL|
00001260 6a 4f 6f 4c 70 6a 77 6d 78 58 77 3d 22 2c 22 64 |jOoLpjwmxXw=","d|
00001270 69 67 65 73 74 22 3a 22 49 70 34 31 5a 58 70 44 |igest":"Ip41ZXpD|
00001280 76 77 52 76 6d 41 73 33 30 58 69 72 6c 48 65 6d |vwRvmAs30XirlHem|
00001290 57 72 44 67 6c 5c 2f 44 4a 31 36 79 33 31 41 71 |WrDgl\/DJ16y31Aq|
000012a0 66 42 55 6f 3d 22 7d 7d 2c 22 63 6f 6e 66 69 67 |fBUo="}},"config|
000012b0 22 3a 7b 22 6a 73 6f 6e 5f 73 69 7a 65 22 3a 22 |":{"json_size":"|
000012c0 31 32 32 38 38 22 2c 22 6b 65 79 73 6c 6f 74 73 |12288","keyslots|
000012d0 5f 73 69 7a 65 22 3a 22 31 36 37 34 34 34 34 38 |_size":"16744448|
000012e0 22 7d 7d 00 00 00 00 00 00 00 00 00 00 00 00 00 |"}}.............|
000012f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00004000
root@disp4117:~#
To get the offset
of the first data segment
we need to check the segments
object in the above JSON. Here's the same segments
section from above, but formatted for readability:
"segments": {
"0": {
"type": "crypt",
"offset": "16777216",
"iv_tweak": "0",
"size": "dynamic",
"encryption": "aes-xts-plain64",
"sector_size": 512
}
},
We can see above that the first data segment
begins at offset
= 16777216
.
Therefore, for the example above, the LUKS2 header ends at byte 16777216.