0

如何以编程方式确定块存储设备上 LUKS 标头的准确起始字节和准确结束字节?

我使用 Linux 统一密钥设置 (LUKS) 进行全盘加密 (FDE),因此我的驱动器上的所有数据都使用不是从我的密码中派生的强主密钥进行加密,并且我正在编写一个安全的脚本在紧急/紧急关机情况下擦除驱动器(即:有人在偷窃您的笔记本电脑)。

假设我有一个 1000T 驱动器,但在我的紧急情况下时间太短(约 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 标头以 byte 4096 * 512=结束2097152

在 LUKS2 中,这要复杂得多,因为它需要解析LUKS 标头中的 JSON 元数据对象。

4

2 回答 2

0

Michael 的回答很好,但它是用 2020 年 1 月 EOL 的 python2 编写的,大多数新发行版默认不再包含它。它可以更新,但 cryptsetup 实际上包含针对这种确切情况的命令。只需运行cryptsetup erase <device>然后键入 YES,它将为您清除标题。您可以使用 跳过确认cryptsetup -q erase <device>

它确实保留了标头,但它擦除了所有关键插槽,这实现了在没有标头备份的情况下使数据不可恢复的目标。

这是手册页https://man.archlinux.org/man/cryptsetup.8

于 2021-03-18T04:23:54.807 回答
0

这是一个快速的 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:~# 
于 2020-03-18T06:47:15.117 回答