Linux Unified Key Setup: Lua parsing library

Linux Unified Key Setup (LUKS) is a format specification for storing disk encryption parameters and up to 8 user keys (which can unlock the master key).

KS implementation details

License: CC0-1.0

References

This page hosts a formal specification of Linux Unified Key Setup using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Lua source code to parse Linux Unified Key Setup

luks.lua

-- This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
--
-- This file is compatible with Lua 5.3

local class = require("class")
require("kaitaistruct")
local str_decode = require("string_decode")
local enum = require("enum")

-- 
-- Linux Unified Key Setup (LUKS) is a format specification for storing disk
-- encryption parameters and up to 8 user keys (which can unlock the master key).
-- See also: Source (https://gitlab.com/cryptsetup/cryptsetup/wikis/LUKS-standard/on-disk-format.pdf)
Luks = class.class(KaitaiStruct)

function Luks:_init(io, parent, root)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root or self
  self:_read()
end

function Luks:_read()
  self.partition_header = Luks.PartitionHeader(self._io, self, self._root)
end

Luks.property.payload = {}
function Luks.property.payload:get()
  if self._m_payload ~= nil then
    return self._m_payload
  end

  local _pos = self._io:pos()
  self._io:seek((self.partition_header.payload_offset * 512))
  self._m_payload = self._io:read_bytes_full()
  self._io:seek(_pos)
  return self._m_payload
end


Luks.PartitionHeader = class.class(KaitaiStruct)

function Luks.PartitionHeader:_init(io, parent, root)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root or self
  self:_read()
end

function Luks.PartitionHeader:_read()
  self.magic = self._io:read_bytes(6)
  if not(self.magic == "\076\085\075\083\186\190") then
    error("not equal, expected " ..  "\076\085\075\083\186\190" .. ", but got " .. self.magic)
  end
  self.version = self._io:read_bytes(2)
  if not(self.version == "\000\001") then
    error("not equal, expected " ..  "\000\001" .. ", but got " .. self.version)
  end
  self.cipher_name_specification = str_decode.decode(self._io:read_bytes(32), "ASCII")
  self.cipher_mode_specification = str_decode.decode(self._io:read_bytes(32), "ASCII")
  self.hash_specification = str_decode.decode(self._io:read_bytes(32), "ASCII")
  self.payload_offset = self._io:read_u4be()
  self.number_of_key_bytes = self._io:read_u4be()
  self.master_key_checksum = self._io:read_bytes(20)
  self.master_key_salt_parameter = self._io:read_bytes(32)
  self.master_key_iterations_parameter = self._io:read_u4be()
  self.uuid = str_decode.decode(self._io:read_bytes(40), "ASCII")
  self.key_slots = {}
  for i = 0, 8 - 1 do
    self.key_slots[i + 1] = Luks.PartitionHeader.KeySlot(self._io, self, self._root)
  end
end


Luks.PartitionHeader.KeySlot = class.class(KaitaiStruct)

Luks.PartitionHeader.KeySlot.KeySlotStates = enum.Enum {
  disabled_key_slot = 57005,
  enabled_key_slot = 11301363,
}

function Luks.PartitionHeader.KeySlot:_init(io, parent, root)
  KaitaiStruct._init(self, io)
  self._parent = parent
  self._root = root or self
  self:_read()
end

function Luks.PartitionHeader.KeySlot:_read()
  self.state_of_key_slot = Luks.PartitionHeader.KeySlot.KeySlotStates(self._io:read_u4be())
  self.iteration_parameter = self._io:read_u4be()
  self.salt_parameter = self._io:read_bytes(32)
  self.start_sector_of_key_material = self._io:read_u4be()
  self.number_of_anti_forensic_stripes = self._io:read_u4be()
end

Luks.PartitionHeader.KeySlot.property.key_material = {}
function Luks.PartitionHeader.KeySlot.property.key_material:get()
  if self._m_key_material ~= nil then
    return self._m_key_material
  end

  local _pos = self._io:pos()
  self._io:seek((self.start_sector_of_key_material * 512))
  self._m_key_material = self._io:read_bytes((self._parent.number_of_key_bytes * self.number_of_anti_forensic_stripes))
  self._io:seek(_pos)
  return self._m_key_material
end