| 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" |
| ] |
| } |
| } |