Device Mapper使用参考


Device Mapper为Linux内核提供了一个从逻辑设备到物理设备的映射框架,通过它,用户可以定制资源的管理策略。

Device Mapper下有Device Mapper Verity和Device Mapper Crypt

  1. DM-Verity(Device Mapper Verity)和DM-Crypt(Device Mapper Crypt)是Linux内核中的两个不同的设备映射器模块,用于提供不同的功能。

  2. DM-Crypt(设备映射加密):DM-Crypt用于对块设备进行加密。它通过在块设备上创建加密映射来工作。加密映射将数据加密存储在底层设备上,并在读取数据时进行解密。DM-Crypt使用各种加密算法和密码来保护数据的机密性。DM-Crypt可用于保护存储设备上的敏感数据,如硬盘驱动器、分区或文件。它可以用来创建加密的文件系统或加密的存储卷,以确保数据在存储和传输过程中的安全性。

1. veritysetup和cryptsetup

veritysetup 和 cryptsetup 是Linux系统中用于配置和管理设备完整性验证(dm-verity)和块设备加密(dm-crypt)的命令行工具。

veritysetup:是用于配置和管理 dm-verity(设备完整性验证)的工具。dm-verity 是 Linux 内核提供的一种功能,可用于验证块设备上的数据的完整性。veritysetup 工具可以用于创建和管理 dm-verity 卷,包括创建根哈希和验证数据块。

cryptsetup:是用于配置和管理 dm-crypt(块设备加密)的工具。dm-crypt 是 Linux 内核提供的一种功能,用于在块设备层级上进行数据加密。cryptsetup 工具可以用于创建和管理加密卷,包括创建加密卷、设置密钥、打开和关闭加密卷,以及执行其他与块设备加密相关的操作。

如没有以上两个app,请联系FAE获取,或者自己编译生成。

2. Device Mapper Verity

dm-verity是Device mapper架构下的一种目标设备类型,是一个虚拟块设备,专门用于文件系统的校验。fs在挂载的时候直接指定 dm-verity 设备,也就是fs直接交互的设备是 dm-verity,dm-verity 调用真正的块驱动去读取对应的块,并计算hash值和hash-tree中对应的hash值进行比较,如果相等,则说明块没有被篡改,返回块数据给fs,如果不相等,则说明块被篡改,系统halt。

Dm-verity规定只能有两个目标设备,一个是数据设备(Data Device),另一个是哈希设备(Hash Device)

img

映射设备(Mapped Device)和目标设备(Target Device)是一对一关系,对映射设备的读操作被映射成对目标设备的读操作,在目标设备中,dm-verity又将读操作映射为数据设备(Data Device)的读操作。

但是在读操作的结束处,dm-verity加了一个额外的校验操作,对读到的数据计算一个hash值,用这个哈希值和存储在哈希设备(Hash Device)中的值做比较,如果不同,则本次读操作被标记为错误。

假设数据设备和哈希设备中每块大小均为4KB,再假设使用hash算法SHA256,即每块数据的哈希值为32B(256bits),则哈希设备中的每块(4KB)存储有4096/32=128个哈希值。所以在layer0中一个哈希设备的块对应数据设备的128个块。

在读取数据时,dm-verity还要防备哈希设备中存储的哈希值被篡改的情况。所以要加上layer1,在layer1中的每块数据对应layer0的128个块,

layer1中的数据就是对layer0中的数据(hash设备和数据设备中的数据)计算hash值,如果layer1中只有一块,那么就此停止,否则继续增加layer,直到layer_n只有一块。最后对layer n再计算hash值,称这个hash值为root hash。这个root hash就可以反应数据设备和hash设备的变化。通过验证root hash 就可以校验数据是否被篡改。

img

dm-verify支持大部分的文件系统,只要文件系统在块设备上,都可以使用dm-verity

2.1 kernel config

  1. DM_verity相关config

    img

  2. SSTAR AES driver配置

    img

2.2 手动测试dm-verity

veritysetup的lib库路径声明

export LD_LIBRARY_PATH=/xxx/yyy/lib:$LD_LIBRARY_PATH

测试下面命令,若没有出错,则代表dm_verity功能正常。

  1. 格式化hash设备,获取root hash值

    ./veritysetup format /dev/mmcblk0p1 /dev/mmcblk0p2 --data-block-size=512 --hash-block-size=512 --debug

  2. 激活verity设备

    ./veritysetup create <name> /dev/mmcblk0p1 /dev/mmcblk0p2 <root_digest> --debug
    

    or:

    ./veritysetup open /dev/mmcblk0p1 <name> /dev/mmcblk0p2 <root_digest> --debug
    
  3. Read:

    dd if=/dev/mapper/ of=test.bin bs=16 count=1

2.3 同一分区实现dm-verity

2.3.1 生成带hash device的文件

PC端生成带hash device的rootfs.sqfs,hash device的位置由--data-block-size的值决定,需要根据实际rootfs.sqfs进行更改。

新rootfs.sqfs尾部会带有hash device信息,注意对旧rootfs进行数据备份,防止参数错误,造成data device数据破坏。

img

2.3.2 rootfs patition size

检查rootfs patition size是否大于等于新生成的rootfs.sqfs size,若小于,则需要手动修改patition,改大rootfs的长度,检查过程如下:

img

修改后的size需满足要求,将新生成的rootfs.sqfs更新到分区中,(network update cmd in uboot :estar scripts/[[rootfs.es)

2.3.3 修改bootargs

原bootargs: ubi.mtd=ubia,2048 root=/dev/mtdblock5 rootfstype=squashfs ro init=/linuxrc LX_MEM=0x1000000000,0x40000000 mma_heap=mma_heap_name0,miu=0,sz=0x10000000 mma_memblock_remove=1 cma=2M mma_heap=mma_heap_fb,miu=0,sz=0x1CF0000 mmap_reserved=fb,miu=0,sz=0x800000,max_start_off=0x3f600000,max_end_off=0x3fe00000 mtdparts=nand0:1792k@1280k(BOOT),1792k(BOOT_BAK),256k(ENV),5m(KERNEL),5m(RECOVERY),6m(rootfs),768k(vendor_storage),1m(MISC),1m(DM-V),106752k(ubia) nohz=off

新bootargs: ubi.mtd=ubia,2048 mtdparts=nand0:1792k@1280k(BOOT),1792k(BOOT_BAK),256k(ENV),5m(KERNEL),5m(RECOVERY),6m(rootfs),768k(vendor_storage),1m(MISC),1m(DM-V),106752k(ubia) console=ttyS0,115200 root=/dev/dm-0 dm-mod.create="dm-verity,,,ro,0 7720 verity 1 /dev/mtdblock5 /dev/mtdblock5 1024 4096 3860 966 sha256 da0c74d6a90ee7aaacc120e3a96a70c68b93ec792549263e25a4b7512c707e44 39745c09881ad80ad6c55da863b06f2f6f16dedc753fde8d073285ca652e275e" LX_MEM=0x1000000000,0x40000000 mma_heap=mma_heap_name0,miu=0,sz=0x10000000 mma_memblock_remove=1 cma=2M mma_heap=mma_heap_fb,miu=0,sz=0x1CF0000 mmap_reserved=fb,miu=0,sz=0x800000,max_start_off=0x3f600000,max_end_off=0x3fe00000 nohz=off

注 mtdparts需要放在dm-0前面,若放在后面,在验证dm-verity时,节点还未生成,导致出错。

参数解释:

dm-mod.create=<name>,<uuid>,<minor>,<flags>,<table>[,<table>+][;<name>,<uuid>,<minor>,<flags>,<table>[,<table>+]+]
参数名字 命令行中设置值 备注
name dm-verity 可自定义
uuid 可选,见5.1 生成文件并烧写,截图中的uuid
minor
flags ro ro=read only rw=read and write
start_sector 0 rootfs.sqfs从第几个block开始计算hash
num_sectors 7720 指的是rootfs.sqfs计算到第几个block。由原始rootfs 长度 3952640/512=7720 《sectors长度固定为512》
target_type verity verity = 验证digest link: https://www.kernel.org/doc/html/next/admin-guide/device-mapper/verity.html
version 1 版本号,当前有0 和1。

This is the type of the on-disk hash format.
This is the type of the on-disk hash format.br>0 is the original format used in the Chromium OS.
The salt is appended when hashing, digests are stored continuously and the rest of the block is padded with zeroes.
1 is the current format that should be used for new devices.
The salt is prepended when hashing and each digest is padded with zeroes to the power of two.
dev /dev/mtdblock5 data device 的节点
hash_dev /dev/mtdblock5 hash device 的节点
data_block_size 1024 data block 设置的长度,固定为512的倍数
hash_block_size 4096 hash block 设置的长度,固定为512的倍数
num_data_blocks 3860 = 实际文件长度/data_block_size = 3952640/1024=3860
hash_start_block 966 当前hash是插在rootfs尾部,所以=3952640/4096 = 965,此为hash第一个block,第一个bolck是hash header,所以真正hash block需要+1
algorithm sha256 使用哪种hash算法
digest da0c74d6a90ee7aaacc120e3a96a70c68b93ec792549263e25a4b7512c707e44 见5.1 生成文件并烧写,截图中的关键信息,即root hash
salt 39745c09881ad80ad6c55da863b06f2f6f16dedc753fde8d073285ca652e275e 见5.1 生成文件并烧写,截图中的关键信息

2.3.4 重启测试

若kernel/rootfs.sqfs已更新,且bootargs已正确设置。则重启,若顺利,则会正常进入kernel,如下图

img

2.4 不同分区实现dm-verity

2.4.1 增加分区

img

2.4.2 PC上执行

veritysetup --data-block-size=1024 format rootfs.sqfs dm_v.bin //rootfs.sqfs为原始binary

2.4.3 dm_v.bin更新到dm_v分区 – estar scripts/[[dmv.es

img

2.4.4 修改bootargs

ubi.mtd=ubia,2048 mtdparts=nand0:1792k@1280k(BOOT),1792k(BOOT_BAK),256k(ENV),5m(KERNEL),5m(RECOVERY),6m(rootfs),768k(vendor_storage),1m(MISC),1m(DM-V),106752k(ubia) console=ttyS0,115200 root=/dev/dm-0 dm-mod.create="dm-verity,,,ro,0 7720 verity 1 /dev/mtdblock5 /dev/mtdblock8 1024 4096 3860 1 sha256 da0c74d6a90ee7aaacc120e3a96a70c68b93ec792549263e25a4b7512c707e44 39745c09881ad80ad6c55da863b06f2f6f16dedc753fde8d073285ca652e275e" LX_MEM=0x1000000000,0x40000000 mma_heap=mma_heap_name0,miu=0,sz bootcmd=dcache on; loados nand 0x23000000 KERNEL by_header; bootm 0x23000000; dcache on; loados nand 0x23000000 RECOVERY by_header; bootm 0x23000000;

2.4.5 重启

重启验证,如果顺利,则正常进入kernel

2.5 自动签章脚本部署dm-verity

2.5.1 dm-verity 打包方式与验签流程

目前dm-verity的append data有两种打包方式,即打包到rootfs分区中间(rootfs_middle)或存放在misc分区(misc_partition)。

rootfs_middle方式下rootfs partition的分布如下:

img

验证append data过程中,因为不需要完整的rootfs,load 完整的 rootfs 会有额外的时间开销。如果rootfs addr中检查不到rootfs magic,就会从patition中 load 512byte,读取 header 中的 rootfs size,跳过 size 长度 load append data,再进行append data验签流程后重建bootcmd,此步骤的目的是保证roothash等数据的可信。

另外misc_partition方式下分布情况如下:

img

append data将以文件的形式存放在misc分区中,其中append data在两种方式下分布情况相同,misc_partition方式中,Uboot阶段将直接根据文件名进行读取。

2.5.2 rootfs dm-verity功能配置

  1. 修改 kernel config 以支持dm-verity,具体参考2.1 kernel config

  2. 进入 project/image/config/general 路径下增加 BOOTCMD_ENV,以生成自动验签指令。

    img

    Uboot阶段会设定环境变量,以dm-verity方式下对rootfs进行验证,其原理是通过sigauth_fs命令来对Binary进行验签,BOOTCMD参数解释:

    sigauth_fs <fs_partition_name> <mid or misc> <key_load_addr>;
    

    EX:

    sigauth_fs rootfs mid 0x21000000
    

2.5.3 dm-verity 自动签章脚本

rootfs 支持 dm-verity 方式下的自动签章,会自动生成已签章的Image。 其自动签章支持两种方式,即全流程自动生成方式,以及rootfs单独生成方式。

2.5.3.1 dm-verity 全流程自动生成

dm-verity下全流程自动生成方式依靠makefile,实际操作步骤如下:

  1. 进入project目录,make xxx_defconfig; make clean -j32; make image-nocheck -j32;

  2. 进入project/image/security_boot_tools/下,修改dm_verity.config文件。

    dm_verity.config文件中参数说明(是否使用 dm-verity 验证 rootfs 取决于该文件):

    Name Description
    rootfs_dm_verity 选择是否打开dm-verify流程,当 rootfs_dm_verity=enable 表示开,此时rootfs不走 secure boot 流程
    rootfs_UUID 自动填充,hash device的UUID,如果 special_cmd 包含"--debug --no-superblock" 则为空
    rootfs_Start_sector 表示rootfs起始扇区,根据rootfs的patition,一般rootfs放在该patition第一个block,即0
    rootfs_Num_sectors 自动填充,表示rootfs扇区个数,每个扇区包含512个字节
    rootfs_Data_block_size 自动填充,表示rootfs数据块大小,默认为4kb,固定为512的倍数
    rootfs_Hash_block_size 自动填充,表示rootfs哈希块大小,默认为4kb,固定为512的倍数
    rootfs_Data_blocks 自动填充,表示rootfs数据块个数
    rootfs_Hash_start_block 自动填充,表示rootfs 哈希设备起始数据块
    rootfs_Hash_algorithm 自动填充,哈希算法类型,默认使用sha256
    rootfs_Salt 自动填充,哈希Salt值
    rootfs_Root_hash 自动填充,root hash 值,当该值为空的时候,会重新veritysetup format的动作,不为空则跳过该动作

    (注:若要重新进行veritysetup的动作,例如修改hash device或改变rootfs size,务必删除root hash的值再进行编译!)

    目前dm-verity的append data的打包方式可以在 project/image/security_boot_tools/Makefile 中修改ROOTFS_PACKET_TYPE关键字(可选misc_partition/rootfs_middle):

    所有修改完成后,在project/image/security_boot_tools/下,make clean;make;即可生成image_secure目录,包含启动流程中所有Image文件(目录下Image已签章)

2.5.3.2 dm-verity rootfs 单独生成

dm-verity下rootfs单独生成方式依靠sh脚本,实际操作步骤如下:

  1. 进入project目录,make xxx_defconfig; make clean -j32; make image-nocheck -j32;

  2. 进入project/image/security_boot_tools/tools路径下,通过auto_generate_dmverity.sh脚本执行命令,执行命令解释:

    ./auto_generate_dmverity.sh [origin rootfs binary] [private key] <output_name>
    

    EX:

    ./auto_generate_dmverity.sh ../../output/images/rootfs.sqfs ../rsa2048/private-image.pem rootfs.sqfs
    

    最后可以得到签章后的rootfs image:

2.5.4 烧录

烧录镜像文件,如果顺利,则正常进入kernel

3. Device Mapper crypt

Dm-crypt是 Linux 内核2.6 及更高版本中的透明块设备加密子系统。它是设备映射器(dm) 基础设施的一部分,并使用来自内核Crypto API的加密例程。dm-crypt 被实现为设备映射器目标,并且可以堆叠在其他设备映射器转换之上。因此,它可以加密整个磁盘(包括可移动媒体)、分区、软件 RAID卷、逻辑卷以及文件。Device Mapper crypt使用LUKS硬盘加密的标准。

LUKS (Linux Unified Key Setup)是 Linux 硬盘加密的标准。 通过提供标准的磁盘格式,它不仅可以促进发行版之间的兼容性,还可以提供对多个用户密码的安全管理。 与现有解决方案相比,LUKS 将所有必要的设置信息存储在分区信息首部中,使用户能够无缝传输或迁移其数据。即Luks是一种磁盘格式

3.1 luks格式

img

aes-cbc-essiv:sha256加密方式的luks header截图

img

其中payload-offset是用户明文数据加密后存放的位置,图中所示为0x800,需要注意payload-offset是以512byte为单位,所以实际上用户明文数据加密后存放的位置为0x800*0x200的位置。

3.2 kernel config

3.2.1 dm_crypt相关config

img

3.2.2 SSTAR AES driver配置

img

3.2.3 分区

新建分区来模拟crypto场景,以下选其一即可

  1. sd卡分区

    img

  2. nand partition

    img

3.3 cryptsetup实现dm-crypt

以下以aes-cbc-essiv:sha256为例。

3.3.1 Cryptsetup的lib库路径声明

export LD_LIBRARY_PATH=/xxx/yyy/lib:$LD_LIBRARY_PATH

3.3.2 创建LUKS1磁盘格式-- 可在L3做此步骤,测试时可在demo board做。

SD卡(ARM demo board):

/mnt/dm_test/cryptsetup/v2.4.1/sbin/cryptsetup -v -c aes-cbc-ess iv:sha256 --key-size 128 luksFormat --type luks1 /dev/mmcblk0p1 --debug

nand(PC):

cryptsetup -v -c aes-cbc-essiv:sha256 --key-size 128 luksFormat --type luks1 dmcrypt.bin --debug

若没有出错,则Command successful. 如下图(截取部分):

img

3.3.3 文件密钥进行解密(可选)

把keyfile加到luks key-slot中

  1. 生成keyfile

    dd if=/dev/random of=/customer/keyfile bs=1024 count=4

  2. 把leyfile加到luks key-slot中

    cryptsetup luksAddKey /dev/mmcblk0p1 /customer/keyfile

  3. 指定key进行解密

    cryptsetup -d /customer/keyfile -s 128 -c aes-cbc-essiv:sha256 create luks /dev/mmcblk0p1

keyfile需要严格保密,以下仅例举可能存放的位置,需要用户根据实际场景,选择合适的存放位置。

  1. 一般有放再root目录下,/etc/目录下,需要确保文件权限的正确设置(如仅root访问)

  2. USB 闪存中,需要满足客户场景。

3.3.4 映射Luks卷

此操作需要输入创建时输入的密码:

SD: /mnt/dm_test/cryptsetup/v2.4.1/sbin/cryptsetup luksOpen /dev/mmcblk0p1 luks

Nand: /mnt/dm_test/cryptsetup/v2.4.1/sbin/cryptsetup luksOpen /dev/mtdblock8 luks

3.3.5 创建文件系统

在Luks磁盘上创建vfat文件系统,仅在第一次时需要此步骤。

mkfs.vfat /dev/mapper/luks

3.3.6 mount文件系统

mount -t vfat /dev/mapper/luks /tmp

此时即可在tmp目录下进行正常的数据读写

写入的文件将使用cbc的加密后写入到SD/Nand中,读数据时将使用CBC解密后再将数据显示出来。SD卡扇区号经过ECB加密作为CBC使用的IV值使用。

3.4 Cryptsetup相关命令

  1. 测试基本加密模型

    cryptsetup benchmark

  2. 指定测试加密模型

    cryptsetup benchmark --cipher aes-cbc

  3. 将磁盘格式化成Luks,并指定加密方式

    aes-xts-plain64:

    cryptsetup -v -c aes-xts-plain64 --key-size 512 --hash sha512 luksFormat --type luks1 /dev/mmcblk0p1
    

    aes-cbc-essiv:sha256:

    cryptsetup -v -c aes-cbc-essiv:sha256 --key-size 128 luksFormat --type luks1 /dev/mmcblk0p1
    
  4. 查看luks状态:

    cryptsetup -v status luks

  5. 查看luks信息:

    cryptsetup luksDump /dev/mmcblk0p1

  6. 测试卷是否为luks:

    cryptsetup -v isLuks /dev/mmcblk0p1

  7. 添加密码

    cryptsetup luksAddKey --key-slot 7 /dev/mmcblk0p1 keyfile

  8. 备份luks头

    cryptsetup luksHeaderBackup /dev/mmcblk0p1 --header-backup-file /tmp/header.bin

  9. 恢复luks头

    cryptsetup luksHeaderRestore /dev/mmcblk0p1 --header-backup-file /mnt/header.bin

  10. 覆盖luks标头

    head -c <number> /dev/zero > /dev/mmcblk0p1
    
  11. 关闭映射

    cryptsetup close /dev/mmcblk0p1