blob: 2ec59191cf77473c18a96a33545a3bf29bd33c02 [file] [log] [blame]
LUKS2 on-disk format
====================
Note: these are temporary documentation notes only.
The more formal definition will be published later.
Design goals
~~~~~~~~~~~~
The LUKS2 is an on-disk storage format designed to
provide simple key management, primarily intended for Full Disk
Encryption based on dm-crypt.
The LUKS2 is highly inspired by LUKS1 format and in some
specific situations (most of the default installations) can be converted
in-place (in both ways - to and from LUKS1).
The LUKS2 format is designed to allow future updates of various
parts without the need to modify binary structures.
On-disk format provides redundancy of metadata, detection
of metadata corruption and automatic repair from metadata copy.
NOTE: For security reasons, there is no redundancy in keyslots
binary data (encrypted keys) but format allows updating to redundant
keyslot encryption in future (add forward error correction codes
is one possibility).
On-disk structure
~~~~~~~~~~~~~~~~~
The LUKS2 header contains three parts:
- binary header (one 4096 bytes sector)
- area for metadata stored in JSON format
- keyslot area (per-context binary data).
The binary header and JSON area are stored twice to increase
redundancy. Keyslot area is allocated per-demand, and it is stored only once.
The basic on-disk structure is then
0 4096
| bin hdr1 | JSON ... | bin hdr2 | JSON ... | Keyslot data | <padding> | (data payload)
Binary header
~~~~~~~~~~~~~
The binary header is intended for quick scanning (by blkid and udev) and contains
magic string to detect the device, basic information (labels), header size information
and metadata checksum.
Checksum covers both binary data and following JSON area and is calculated
with checksum fields zeroed. By default plain SHA256 checksum is used.
The primary binary header is always stored in sector 0 of the device.
The C structure of binary header (see luks2.h) is
#define LUKS2_MAGIC_1ST "LUKS\xba\xbe"
#define LUKS2_MAGIC_2ND "SKUL\xba\xbe"
#define LUKS2_MAGIC_L 6
#define LUKS2_UUID_L 40
#define LUKS2_LABEL_L 48
#define LUKS2_SALT_L 64
#define LUKS2_CHECKSUM_ALG_L 32
#define LUKS2_CHECKSUM_L 64
struct luks2_hdr_disk {
char magic[LUKS2_MAGIC_L]; /* "LUKS\xba\xbe" (1st) or "SKUL\xba\be" (2nd) */
uint16_t version; /* Version 2 */
uint64_t hdr_size; /* in bytes, including JSON area */
uint64_t seqid; /* sequence ID, increased on every update */
char label[LUKS2_LABEL_L]; /* ASCII label or empty */
char checksum_alg[LUKS2_CHECKSUM_ALG_L]; /* checksum algorithm, "sha256" */
uint8_t salt[LUKS2_SALT_L]; /* random salt, unique for every header */
char uuid[LUKS2_UUID_L]; /* UUID of device */
char subsystem[LUKS2_LABEL_L]; /* owner subsystem label or empty */
uint64_t hdr_offset; /* header offset from device start in bytes */
char _padding[184]; /* must be zeroed */
uint8_t csum[LUKS2_CHECKSUM_L]; /* header checksum */
char _padding4096[7*512]; /* must be zeroed */
} __attribute__ ((packed));
The LUKS1 compatible field (magic, UUID) are placed intentionally on the same offsets.
The header version must be set to 2.
The UUID is the same format as in LUKS1.
Magic string differs between the first and second header.
The hdr_offset must match physical header offset on the device.
If hdr_offset does not match, the header is misplaced and must not be used.
(It is a prevention to partition resize or manipulation with device start offset.)
The hdr_size contains the size of the binary header and JSON data area.
The offset and size of the second (backup) header must match to these data.
(Prevention to rewrite of a header with different JSON area size.)
There are two labels - label and subsystem. Content of these fields will be visible
in UDEV/blkid scan and can be used for similar purposes as a filesystem label.
These fields are by default empty.
The salt field in binary header is generated by an RNG and is different for
every header, even the backup header must contain a different salt.
The salt in binary header is not used after the header is read, the main intention
is to avoid deduplication of the header sector.
The salt must be regenerated on every header repair (but not on regular update).
The sequential number (seqid) is a counter that is always increased when a new
update of the header is written. The header with higher seqid is more recent and
is used for recovery (if there are two headers with different seqid, the
more recent one is automatically used).
The rest of binary header must be zeroed.
JSON area
~~~~~~~~~
The JSON area starts immediately after the binary header. Its size is set
by binary header hdr_size field (JSON area size = hdr_size - 4096).
The area contains metadata in JSON format and is fixed. Unused remainder
of the area must be empty.
The header cannot store larger metadata that this fixed buffer and header
size must be set properly during format. For now, only areas with 14 kB
header (4kB binary header + 14kB JSON area) is created during format.
The JSON is structured to be able to describe system in very generic way,
but LUKS2 intentionally limits options to values that are supportable
in implemented version.
JSON structure is as follows:
Mandatory sections (must be present but some can be empty):
- config
- keyslots
- digests
- segments
- tokens
Except for config section, all section contains array of objects that must be named
as number (unsigned integer) - for example keyslot "0", "1" etc.
Every object is typed (must contain attribute "type").
According to type, library decides how to handle (or ignore) such an object.
Binary data inside JSON (for example salt) is stored in Hexa64 encoding.
If a value is needed to be stored as a 64bit integer (usually offset or size),
it is stored in text format and later converted to the 64bit integer.
(JSON cannot store 64bit integers directly.)
Config section
~~~~~~~~~~~~~~
Config contains information about JSON buffer size (cross-checked with binary header),
keyslot area size and optional object with activation flags.
The "flags" section is array of activation flags that are automatically used
when LUKS device is activated (for example it can unconditionally allow TRIM/discard
functionality on the encrypted device).
Segments sections
~~~~~~~~~~~~~~~~~
The segment is an encrypted area on the disk containing data (in LUKS1 often
mentioned as a data payload).
For now, only one data area is available for the user.
(More segments will be later used for on-line re-encryption functionality.)
Segments contain definition about encryption parameters, sector size and
start and length of the segments. By default, the segment starts directly
after the LUKS2 header and is marked as "dynamic" (it automatically detects
the size of the available device).
Optionally it can contain information about data integrity protection,
then the data segments is formatted as dm-integrity device and dm-crypt
encryption is stacked above.
To activate a segment, there must be at least one digest linked to it.
Keyslots section
~~~~~~~~~~~~~~~~
Keyslot object contains information stored key - area, where it is stored
(keyslot data), encryption, anti-forensic function, and Key Derivation Function
and its parameters (PBKDF type, costs, salt).
For now, only internal "luks2" keyslot type is available, it uses the same logic
as LUKS1 keyslot, but allows to define per-keyslot algorithms
(for example different PBKDF).
Digests section
~~~~~~~~~~~~~~~
The digest is used to verify that volume key decrypted from a keyslot is correct.
A digest is linked to keyslots and segment.
For now, only "pbkdf2" digest (LUKS1 compatible digest that uses PBKDF2)
is supported.
Tokens section
~~~~~~~~~~~~~~
A token is an object that can describe "how to get passphrase or key" to unlock
particular keyslot or it can be used t store any additional data (even unrelated
to a keyslot).
This area can be user configurable, and libcryptsetup provides interface to
store used data directly in JSON format.
Some token types are implemented internally, for now, there is only "luks2-keyring".
type. This token type tries to load unlocking passphrase from kernel keyring
with stored identification.
There can be external application that uses token objects to store metadata and
implements bindings to specific hardware (TPM etc.).
LUKS2 JSON Format Example
~~~~~~~~~~~~~~~~~~~~~~~~~
For illustration this is example of a LUKS2 device JSON:
{
"keyslots":{
"0":{
"type":"luks2",
"key_size":32,
"kdf":{
"type":"argon2i",
"time":181,
"memory":1024,
"cpus":4,
"salt":"Xfc5ScS8tCLrdbt6jtyWsBjCwAn3Msn\/enOYaAq8PEo="
},
"af":{
"type":"luks1",
"hash":"sha256",
"stripes":4000
},
"area":{
"type":"raw",
"encryption":"aes-xts-plain64",
"key_size":32,
"offset":"32768",
"size":"131072"
}
}
},
"tokens":{
"0":{
"type":"luks2-keyring",
"keyslots":[
"0"
],
"key_description":"my-token"
}
},
"segments":{
"0":{
"type":"crypt",
"offset":"4194304",
"iv_tweak":"0",
"size":"dynamic",
"encryption":"aes-xts-plain64",
"sector_size":512
}
},
"digests":{
"0":{
"type":"pbkdf2",
"keyslots":[
"0"
],
"segments":[
"0"
],
"hash":"sha256",
"iterations":155298,
"salt":"WgMOideLECc5hfnmFVu3bwttJpkfnpf2RayE2WhP8zU=",
"digest":"olobPk9pc0GItqofH78aMPmRaOZIbRevlvSlTZ91NLI="
}
},
"config":{
"json_size":"12288",
"keyslots_size":"4161536",
"flags":[
"allow-discards"
]
}
}