Как программно найти начальный и конечный байты заголовка LUKS? - PullRequest
0 голосов
/ 18 марта 2020

Как программно определить точный начальный байт и точный конечный байт заголовка LUKS на устройстве хранения блоков?

Я использую Linux Унифицированная настройка ключа (LUKS) для полного шифрования диска (FDE) Таким образом, все данные на моем диске зашифрованы с использованием надежного главного ключа, который не получен из моего пароля, и я работаю над сценарием, который будет безопасно очищать диск в ситуации паники / аварийного отключения (ie: кто-то физически крадет ваш ноутбук).

Допустим, у меня накопитель на 1000 т, и время в моем аварийном сценарии слишком короткое (~ 30 секунд), чтобы фактически заполнить диск случайными байтами. Вместо этого, я просто хотел бы перезаписать заголовок, потому что все данные на диске бесполезны, если заголовок (содержащий слоты ключей с главным ключом и солями) потерян - даже если пароль был восстановлен с помощью криптоанализа с резиновым шлангом .

Как программно безопасно определить начальный и конечный байты заголовка LUKS, чтобы я знал, что следует перезаписать?

Примечание: предоставленное решение должно быть действительным для LUKS1 (выпущено в 2014 году) и LUKS2 (выпущено в 2018 году).

В LUKS1 я обнаружил, что начало равно 0, и конец определяется путем умножения поля payload-offset из двоичного заголовка контейнера на 512. Например,

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:~# 

В этом случае заголовок LUKS1 заканчивается байтом 4096 * 512 = 2097152 .

В LUKS2 это намного сложнее, так как требует синтаксического анализа JSON объекта метаданных в заголовке LUKS.

1 Ответ

0 голосов
/ 18 марта 2020

Вот быстрый python скрипт, который выведет начальный и конечный байты для данного контейнера LUKS:

#!/usr/bin/python
################################################################################
# File:    luksHeaderBounds.py
# Purpose: Determine the start and end bytes of a LUKS header
# Authors: Michael Altfield <michael@michaelaltfield.net>
# Created: 2020-03-18
# Updated: 2020-03-18
# Version: 0.1
################################################################################

# DEPENDS
import sys, struct, json;

# MAIN BODY

if len(sys.argv) != 2:
    print "Usage: luksHeaderBounds.py <device>"
    exit(1)

device = sys.argv[1]

# first we get the LUKS version
with open(device, "rb") as f:

    # the LUKS version field is a big endian unsigned short (uint16_t = '>H')
    #  * https://docs.python.org/2/library/struct.html

    # the LUKS version field starts at offset 6 bytes and is 2 bytes long
    # so we get the first 8 bytes and the last 2 bytes of that
    #  * https://gitlab.com/cryptsetup/LUKS2-docs/blob/master/luks2_doc_wip.pdf

    luksVersion = struct.unpack( '>H', f.read(8)[-2:] )[0];

if luksVersion == 1:
    # LUKS1 - https://gitlab.com/cryptsetup/cryptsetup/-/wikis/LUKS-standard/on-disk-format.pdf

    with open(device, "rb") as f:

        # the payload-offset field is a big endian unsigned long (uint32_t = '>L')
        #  * https://docs.python.org/2/library/struct.html

        # in LUKS1, the payload-offset field starts at offset 104 bytes and is 4 bytes long
        # so we get the first 108 bytes and the last 4 bytes of that
        #  * https://gitlab.com/cryptsetup/cryptsetup/-/wikis/LUKS-standard/on-disk-format.pdf

        payloadOffset = struct.unpack( '>L', f.read(108)[-4:] )[0];

    # the payload-offset is just the number of (512-byte sized) sectors
    # to get bytes we must multiply it by 512
    luksHeaderEnd = 512 * payloadOffset

elif luksVersion == 2:
    # LUKS2 - https://gitlab.com/cryptsetup/LUKS2-docs/blob/master/luks2_doc_wip.pdf

    # first we get the header size from the primary binary header
    with open(device, "rb") as f:

        # the hdr_size field is a big endian unsigned long long (uint64_t = '>Q')
        #  * https://docs.python.org/2/library/struct.html

        # in LUKS2, the hdr_size field starts at offset 8 bytes and is 8 bytes long
        # so we get the first 16 bytes and the last 8 bytes of that
        #  * https://gitlab.com/cryptsetup/LUKS2-docs/blob/master/luks2_doc_wip.pdf

        hdr_size=struct.unpack( '>Q', f.read(16)[-8:] )[0];

    # the JSON plaintext metadata object starts after the header (4096 bytes)
    # and ends at (hdr_size - 4096)
    jsonSize = hdr_size - 4096;

    with open(device, "rb") as f:
        luksMetadata = f.read(hdr_size)[-jsonSize:];

    decoder = json.JSONDecoder()
    jsonMetadata = decoder.raw_decode(luksMetadata)

    # the LUKS2 header ends at the start of the first data segment
    luksHeaderEnd=jsonMetadata[0]['segments']['0']['offset']

else:

    print "ERROR: Unable to determine LUKS version"
    exit(1)

print "Device: " +str(device)
print "  LUKS Header Start Byte: " +str(0)
print "  LUKS Header End Byte:   " +str(luksHeaderEnd)

exit(0)

И пример выполнения для тома LUKS1:

root@disp4117:~# ./luksHeaderBounds.py luksVol1
Device: luksVol1
  LUKS Header Start Byte: 0
  LUKS Header End Byte:   2097152
root@disp4117:~# 

И пример выполнения для тома LUKS2:

root@disp4117:~# ./luksHeaderBounds.py luksVol2
Device: luksVol2
  LUKS Header Start Byte: 0
  LUKS Header End Byte:   16777216
root@disp4117:~# 
...