Json 如何通过编程查找LUKS头的开始字节和结束字节?

Json 如何通过编程查找LUKS头的开始字节和结束字节?,json,linux,encryption,luks,Json,Linux,Encryption,Luks,如何以编程方式确定块存储设备上LUKS头的确切开始字节和确切结束字节 我使用Linux统一密钥设置(LUKS)进行全磁盘加密(FDE),因此,我的驱动器上的所有数据都使用不是从我的密码中派生的强主密钥进行加密,并且我正在编写一个脚本,该脚本将在紧急/紧急关机情况下安全地擦除驱动器(即:有人实际偷走了你的笔记本电脑) 假设我有一个1000T驱动器,在紧急情况下,时间太短(~30秒),无法用随机字节填充驱动器。相反,我只想覆盖标头,因为如果标头(包含带有主密钥和salt的键槽)丢失,驱动器上的所有数

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

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

假设我有一个1000T驱动器,在紧急情况下,时间太短(~30秒),无法用随机字节填充驱动器。相反,我只想覆盖标头,因为如果标头(包含带有主密钥和salt的键槽)丢失,驱动器上的所有数据都将一文不值——即使密码短语是通过橡皮软管密码分析恢复的

如何通过编程安全地确定LUKS头的开始字节和结束字节,以便知道要覆盖什么

注意:提供的解决方案必须同时适用于(2014年发布)和(2018年发布)。

在LUKS1中,我发现开始是0,结束是通过将容器二进制报头的
有效负载偏移量
字段乘以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中,它比在LUKS头中需要的复杂得多。

下面是一个快速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)
以及LUKS2卷的示例执行:

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

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

它确实保留了报头,但它清除了所有的键槽,从而实现了在没有报头备份的情况下使数据无法恢复的目标


这里是手册页

密码设置擦除的问题在于它没有提供合理的可否认性。我开发了这个脚本,作为我与BusKill合作的一部分。脚本本身存在于github中。在这里,使用
cryptsetup
打开了一个票证,以更改此行为,但它被标记为不会修复
root@disp4117:~# ./luksHeaderBounds.py luksVol2
Device: luksVol2
  LUKS Header Start Byte: 0
  LUKS Header End Byte:   16777216
root@disp4117:~#