Device Mapper使用参考
Device Mapper为Linux内核提供了一个从逻辑设备到物理设备的映射框架,通过它,用户可以定制资源的管理策略。
Device Mapper下有Device Mapper Verity和Device Mapper Crypt
-
DM-Verity(Device Mapper Verity)和DM-Crypt(Device Mapper Crypt)是Linux内核中的两个不同的设备映射器模块,用于提供不同的功能。
-
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)
映射设备(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 就可以校验数据是否被篡改。
dm-verify支持大部分的文件系统,只要文件系统在块设备上,都可以使用dm-verity
2.1 kernel config¶
-
DM_verity相关config
-
SSTAR AES driver配置
2.2 手动测试dm-verity¶
veritysetup的lib库路径声明
export LD_LIBRARY_PATH=/xxx/yyy/lib:$LD_LIBRARY_PATH
测试下面命令,若没有出错,则代表dm_verity功能正常。
-
格式化hash设备,获取root hash值
./veritysetup format /dev/mmcblk0p1 /dev/mmcblk0p2 --data-block-size=512 --hash-block-size=512 --debug
-
激活verity设备
./veritysetup create <name> /dev/mmcblk0p1 /dev/mmcblk0p2 <root_digest> --debug
or:
./veritysetup open /dev/mmcblk0p1 <name> /dev/mmcblk0p2 <root_digest> --debug
-
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数据破坏。
2.3.2 rootfs patition size¶
检查rootfs patition size是否大于等于新生成的rootfs.sqfs size,若小于,则需要手动修改patition,改大rootfs的长度,检查过程如下:
修改后的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,如下图
2.4 不同分区实现dm-verity¶
2.4.1 增加分区¶
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¶
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的分布如下:
验证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方式下分布情况如下:
append data将以文件的形式存放在misc分区中,其中append data在两种方式下分布情况相同,misc_partition方式中,Uboot阶段将直接根据文件名进行读取。
2.5.2 rootfs dm-verity功能配置¶
-
修改 kernel config 以支持dm-verity,具体参考2.1 kernel config。
-
进入 project/image/config/general 路径下增加 BOOTCMD_ENV,以生成自动验签指令。
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,实际操作步骤如下:
-
进入project目录,make xxx_defconfig; make clean -j32; make image-nocheck -j32;
-
进入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脚本,实际操作步骤如下:
-
进入project目录,make xxx_defconfig; make clean -j32; make image-nocheck -j32;
-
进入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格式¶
aes-cbc-essiv:sha256加密方式的luks header截图
其中payload-offset是用户明文数据加密后存放的位置,图中所示为0x800,需要注意payload-offset是以512byte为单位,所以实际上用户明文数据加密后存放的位置为0x800*0x200的位置。
3.2 kernel config¶
3.2.1 dm_crypt相关config¶
3.2.2 SSTAR AES driver配置¶
3.2.3 分区¶
新建分区来模拟crypto场景,以下选其一即可
-
sd卡分区
-
nand partition
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. 如下图(截取部分):
3.3.3 文件密钥进行解密(可选)¶
把keyfile加到luks key-slot中
-
生成keyfile
dd if=/dev/random of=/customer/keyfile bs=1024 count=4
-
把leyfile加到luks key-slot中
cryptsetup luksAddKey /dev/mmcblk0p1 /customer/keyfile
-
指定key进行解密
cryptsetup -d /customer/keyfile -s 128 -c aes-cbc-essiv:sha256 create luks /dev/mmcblk0p1
keyfile需要严格保密,以下仅例举可能存放的位置,需要用户根据实际场景,选择合适的存放位置。
-
一般有放再root目录下,/etc/目录下,需要确保文件权限的正确设置(如仅root访问)
-
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相关命令¶
-
测试基本加密模型
cryptsetup benchmark
-
指定测试加密模型
cryptsetup benchmark --cipher aes-cbc
-
将磁盘格式化成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
-
查看luks状态:
cryptsetup -v status luks
-
查看luks信息:
cryptsetup luksDump /dev/mmcblk0p1
-
测试卷是否为luks:
cryptsetup -v isLuks /dev/mmcblk0p1
-
添加密码
cryptsetup luksAddKey --key-slot 7 /dev/mmcblk0p1 keyfile
-
备份luks头
cryptsetup luksHeaderBackup /dev/mmcblk0p1 --header-backup-file /tmp/header.bin
-
恢复luks头
cryptsetup luksHeaderRestore /dev/mmcblk0p1 --header-backup-file /mnt/header.bin
-
覆盖luks标头
head -c <number> /dev/zero > /dev/mmcblk0p1
-
关闭映射
cryptsetup close /dev/mmcblk0p1