1

In analyzing the LUKS header on an encrypted volume, the cipher-name field shows a single-character = '@'. What does that mean?

The LUKS On-Disk Format Specification originally published by Clemens Fruhwirth in 2005 clearly defines the encoding of the LUKS header, which states that the first three data fields are as follows:

+-------+--------+-------------+
| Start | Length | Field Name  |
+-------+--------+-------------+
|     0 |      6 | magic       |
|     6 |      2 | version     |
|     8 |     32 | cipher-name |
+-------+--------+-------------+

On an older drive, this works as expected, and it's obvious that the cipher-name is "aes"

root@disp8551:~# hexdump -Cs 0 -n 6 luksVol1 
00000000  4c 55 4b 53 ba be                                 |LUKS..|
00000006
root@disp8551:~# hexdump -Cs 6 -n 2 luksVol1 
00000006  00 01                                             |..|
00000008
root@disp8551:~# hexdump -Cs 8 -n 32 luksVol1 
00000008  61 65 73 00 00 00 00 00  00 00 00 00 00 00 00 00  |aes.............|
00000018  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000028
root@disp8551:~# 

But on a newer drive, the only printable character in the cipher-name field is '@'

root@disp85:~# hexdump -Cs 0 -n 6 luksVol2
00000000  4c 55 4b 53 ba be                                 |LUKS..|
00000006
root@disp85:~# hexdump -Cs 6 -n 2 luksVol2
00000006  00 02                                             |..|
00000008
root@disp85:~# hexdump -Cs 8 -n 32 luksVol2
00000008  00 00 00 00 00 00 40 00  00 00 00 00 00 00 00 03  |......@.........|
00000018  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000028
root@disp85:~# 

What cipher is LUKS using here with @? What does the at symbol in the cipher-name field mean?

  • 1
    Could it be that you're reading a LUKS2 volume based on the LUKS1 format specification? – Arminius Mar 16 at 3:30
4

The cipher-name in your example is not '@'. The field you're looking at is actually the hdr_size field.

The mistake you've made is trying to decode a LUKS2 container with LUKS1 encoding. In the table provided, you've shown that the second field (bytes 6-8) are the version data field. And your hexdump clearly shows that the first LUKS container (luksVol1) has version = 00 01 while the second LUKS container (luksVol2) has version = 00 02. These hex values directly correlate the former to LUKS1 and the latter LUKS2.

Quoting from OP that demonstrates this:

root@disp8551:~# hexdump -Cs 6 -n 2 luksVol1 
00000006  00 01                                             |..|
00000008
...
root@disp85:~# hexdump -Cs 6 -n 2 luksVol2
00000006  00 02                                             |..|
00000008

Your encoding table in the OP was correct only for LUKS1 as defined in the LUKS On-Disk Format Specification published by Clemens Fruhwirth in 2005.

LUKS2 uses a different encoding that's defined in the LUKS2 On-Disk Format Specification published by Milan Brož in 2018. Here's the updated encoding table for LUKS2. Note that binary headers in LUKS2 must be exactly 4096 bytes long.

+-------+--------+--------------+
| Start | Length |  Field Name  |
+-------+--------+--------------+
|     0 |      6 | magic        |
|     6 |      2 | version      |
|     8 |      8 | hdr_size     |
|    16 |      8 | seqid        |
|    24 |     48 | label        |
|    72 |     32 | csum_alg     |
|   104 |     64 | salt         |
|   168 |     40 | uuid         |
|   208 |     48 | subsystem    |
|   256 |      8 | hdr_offset   |
|   264 |    184 | _padding     |
|   448 |     64 | csum         |
|   512 |   3584 | _padding4096 |
+-------+--------+--------------+

And here's the relevant hexdumps for the data fields shown above on an example LUKS2 container's primary header.

# magic
root@disp4117:~# hexdump -Cs 0 -n 6 luksVol2
00000000  4c 55 4b 53 ba be                                 |LUKS..|
00000006

# version
root@disp4117:~# hexdump -Cs 6 -n 2 luksVol2
00000006  00 02                                             |..|
00000008

# hdr_size
root@disp4117:~# hexdump -Cs 8 -n 8 luksVol2
00000008  00 00 00 00 00 00 40 00                           |......@.|
00000010

# seqid
root@disp4117:~# hexdump -Cs 16 -n 8 luksVol2
00000010  00 00 00 00 00 00 00 03                           |........|
00000018

# label
root@disp4117:~# hexdump -Cs 24 -n 48 luksVol2
00000018  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000048

# csum_alg
root@disp4117:~# hexdump -Cs 72 -n 31 luksVol2
00000048  73 68 61 32 35 36 00 00  00 00 00 00 00 00 00 00  |sha256..........|
00000058  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00     |...............|
00000067

# salt
root@disp4117:~# hexdump -Cs 104 -n 64 luksVol2
00000068  4f b1 7c b2 bf 7d da 1f  15 64 df cc ab 74 68 5c  |O.|..}...d...th\|
00000078  9a f9 3d 02 42 67 0f 92  ee 1d bf 98 11 5b 3d b8  |..=.Bg.......[=.|
00000088  87 e4 e0 a8 76 23 68 86  2f fc e1 98 a7 a1 5a a9  |....v#h./.....Z.|
00000098  b6 c5 e5 e2 9a 87 f7 ff  55 52 dc 07 07 dd fa a4  |........UR......|
000000a8

# uuid
root@disp4117:~# hexdump -Cs 168 -n 40 luksVol2
000000a8  65 63 30 32 36 64 64 66  2d 37 37 36 32 2d 34 34  |ec026ddf-7762-44|
000000b8  64 63 2d 39 65 37 30 2d  62 35 36 66 36 61 65 62  |dc-9e70-b56f6aeb|
000000c8  63 31 32 38 00 00 00 00                           |c128....|
000000d0

# subsystem
root@disp4117:~# hexdump -Cs 208 -n 48 luksVol2
000000d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000100

# hdr_offset
root@disp4117:~# hexdump -Cs 256 -n 8 luksVol2
00000100  00 00 00 00 00 00 00 00                           |........|
00000108

# _padding
root@disp4117:~# hexdump -Cs 264 -n 184 luksVol2
00000108  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*

# csum
000001c0
root@disp4117:~# hexdump -Cs 448 -n 64 luksVol2
000001c0  d0 9a e0 f8 96 ed 8f db  42 5f 58 19 99 2a 72 18  |........B_X..*r.|
000001d0  01 5e f7 81 34 99 f7 c5  17 a2 07 2f 60 be 40 bd  |.^..4....../`.@.|
000001e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000200

# _padding4096
root@disp4117:~# hexdump -Cs 512 -n 3584 luksVol2
00000200  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00001000
root@disp4117:~# 

Note that none of the above fields actually contain the cipher-name. In LUKS2, that's stored in the JSON object. In LUKS2, there's actually six header areas:

  \/ primary binary header          alignment padding \/
+----+----------+----+----------+-------------------+----+
| /\ | 1st JSON | \/ | 2nd JSON |   Keyslots area   | /\ |
+----+----------+----+----------+-------------------+----+
                  /\ secondary binary header

The hexdump shown above only shows the "primary binary header" area. To get the cipher-name, we must look at the JSON area.

In LUKS2, there's actually two copies of the data 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.

While the "1st JSON" area always starts at byte 4096 (immediately after the "primary binary header" area), its length is variable. The length can be determined by looking at the binary header's hdr_size (ie the same field that you confused for the cipher-name). 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 cipher-name specific for encrypting the data in the LUKS container (as opposed to the cipher used to symmetrically encrypt the a given keyslot), 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
    }
  },

LUKS supports many distinct segments, but this LUKS container only has one. The attribute we want is encryption, which has a value in the dm-crypt notation. In this case, it's aes-xts-plain64 -- which means the cipher-name is aes and the cipher-mode is xts-plain64.

This also matches the cipher listed in a cryptsetup luksDump on the LUKS2 container

root@disp4117:~# cryptsetup luksDump luksVol2 | grep -i cipher | head -n1
    cipher: aes-xts-plain64
root@disp4117:~# 
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.