rk3568安全启动功能实践

        本文主要讲述笔者在rk3568芯片上开发安全启动功能实践的流程。其中主要参考瑞芯微官方文档《Rockchip_Developer_Guide_Secure_Boot_for_UBoot_Next_Dev_CN.pdf》。文档中描述逻辑不是很清晰而且和当前瑞芯微的sdk中安全启动的流程匹配度不高。本文就不再对瑞芯微官方文档的内容进行赘述,读者可以先查看官方文件后再阅读本文章。

        所谓的安全验证引导流程分为安全性校验与完整性校验。安全性校验是加密公钥的校验,流程为从安全存储(OTP & efuse)中读取公钥 hash,与计算的公钥 hash 对比,是否一致,然后公钥用于解密固件hash。完整性校验为校验固件的完整性,流程为从存储里加载固件,计算固件的hash与解密出来的hash对比是否一致。

1、使用什么方案?

        Linux 系统中,提供了新旧2种Secure Boot方案,FIT和AVB方案,效果是一样的,两种方案不可混用,各个芯片具体选用那种方案,请参考产品版本。

rk3568安全启动功能实践_第1张图片

  从上面的图中可知RK356X kernel校验方式为FIT,安全分区为OTP。AVB和FIT的差异以及EFUSE和OTP的差异在瑞芯微官方文档中有详细描述这里不再赘述,只是希望读者不要使用错误的方案。在配置时设置RK_SECUREBOOT_FIT=y ,不要配置RK_SECUREBOOT_AVB=y,因为rk3568芯片不支持AVB方案。

        FIT(flattened image tree)是U-Boot⽀持的⼀种新固件类型的引导⽅案,⽀持任意多个image打包和校验。FIT使⽤its(image source file)⽂件描述image信息,最后通过mkimage⼯具⽣成itb(flattened image tree blob)镜像。its⽂件使⽤DTS的语法规则,⾮常灵活,可以直接使⽤libfdt 库和相关⼯具。同时自带一套全新的安全检验方式。

2、对uboot进行签名

2.1、生成密钥对

        在对镜像进行签名之前,需要生成秘钥对,作为对进行签名的密钥对。

        U-Boot工程下执行如下三条命令可以生成签名用的RSA密钥对。通常情况下只需要生成一次,此后都用这对密钥签名和验证固件,请妥善保管。

        第一步:放key的目录:keys

        mkdir -p keys

        第二步:使用RK的"rk_sign_tool"工具生成RSA2048的私钥privateKey.pem和publicKey.pem,分别更名存放为:keys/dev.key和keys/dev.pubkey。命令为:

        ../rkbin/tools/rk_sign_tool kk --bits 2048 --out .

        ln -s privateKey.pem keys/dev.key

        ln -s publicKey.pem keys/dev.pubkey

        第三步:使用-x509和私钥生成一个自签名证书:keys/dev.crt (效果本质等同于公钥)

        openssl req -batch -new -x509 -key keys/dev.key -out keys/dev.crt

注意:上述的"keys"、"dev.key"、"dev.crt" 、"dev.pubkey"名字都不可变。因为这些名字已经在its

文件中静态定义,如果改变则会打包失败。

        当然上面的步骤可以通过 build.sh security-createkeys命令来实现。

2.2、uboot配置

        U-Boot的defconfig打开如下配置:

        CONFIG_FIT_SIGNATURE=y

        CONFIG_SPL_FIT_SIGNATURE=y

2.3、buildroot的配置

        在瑞芯微sdk目录下执行 make menuconfig->Security feature (secureboot, encryption, verity, etc.)  来进行安全启动相关配置的配置,配置了这些内容后,编译SDK时会自动对uboot和kernel进行签名。

rk3568安全启动功能实践_第2张图片

        如果想这些配置一开始就生效,可以在 SDK所在目录的device/rockchip/rk3566_rk3568/rockchip_rk3568_evb1_ddr4_v10_defconfig 中添加fit相关的配置(rockchip_rk3568_evb1_ddr4_v10_defconfig 是笔者当前系统使用的配置,读者需要根据自己编译SDK时选择的配置项修改对应的配置文件):

RK_UBOOT_SPL=y

RK_SECURITY=y

#

# Security check method (system-verity) needs squashfs rootfs type

#

RK_SECUREBOOT_METHOD="fit"

RK_SECUREBOOT_FIT=y

# RK_SECUREBOOT_AVB is not set

RK_SECURITY_OPTEE_STORAGE="rpmb"

RK_SECURITY_OPTEE_STORAGE_RPMB=y

# RK_SECURITY_OPTEE_STORAGE_SECURITY is not set

RK_SECURITY_CHECK_METHOD="base"

RK_SECURITY_CHECK_BASE=y

# RK_SECURITY_CHECK_SYSTEM_ENCRYPTION is not set

# RK_SECURITY_BURN_KEY is not set

3、对kernle进行签名

        在kernel内核配置文件rockchip_linux_defconfig文件中添加如下配置:

CONFIG_BLK_DEV_DM=y

CONFIG_DM_CRYPT=y

CONFIG_BLK_DEV_CRYPTOLOOP=y

CONFIG_DM_VERITY=y

        将optee设备树配置信息添加到rk3568.dtsi设备树中

optee: optee {

        compatible = "linaro,optee-tz";

        method = "smc";

        status = "okay";

};

        内核开启CONFIG_DM_VERITY=y的情况下,parameter-buildroot-fit.txt分区配置脚本中GROW_ALIGN: 1必须设置为1.具体可以查看SDK中的check-grow-align.sh 脚本,该脚本存在如下程序进行检查:

# DM_VERITY存在且非空并并且GROW_ALIGN_VAL值为1,则exit 0

if [ "$DM_VERITY" -a "$GROW_ALIGN_VAL" = "1" ]; then

        # DM verity + grow align

        exit 0

fi

        不配置的话,编译时也会报错提醒,根据提示信息修改即可。感兴趣的同学可以通读这个脚本。

4、验证

        注意,我们还没有烧录公钥的hash值到OTP区。执行SDK的全编译,将编译后的固件烧录到设备中。公钥hash是否烧写,只会影响Maskrom是否校验loader,且烧录后,无法撤销。loader验证uboot以及后续验证流程还是一致的。因此,调试阶段建议先不用burn-key-hash。

进行验证,关键打印信息如下:
Trying fit image at 0x4000 sector
## Verified-boot: 0
sha256,rsa2048:dev## Verified-boot: 0

## Checking atf-1 0x00040000 (gzip @0x00240000) ... sha256(ee9d731c06...) + sha256(b5946ac63d...) + OK
## Checking uboot 0x00a00000 (gzip @0x00c00000) ... sha256(49f841525c...) + sha256(30f02600a8...) + OK
## Checking fdt 0x00b53b50 ... sha256(f133b1d7de...) + OK
## Checking atf-2 0xfdcc1000 ... sha256(b8dca786b4...) + OK
## Checking atf-3 0x0006b000 ... sha256(2f91089eb7...) + OK
## Checking atf-4 0xfdcce000 ... sha256(86ef885748...) + OK
## Checking atf-5 0xfdcd0000 ... sha256(0b2b146c60...) + OK
## Checking atf-6 0x00069000 ... sha256(a9a1e63bef...) + OK
## Checking optee 0x08400000 (gzip @0x08600000) ... sha256(8f745f9f51...) + sha256(ac96eda7b3...) + OK

Jumping to U-Boot(0x00a00000) via ARM Trusted Firmware(0x00040000)
Total: 174.537/261.748 ms

......
## Loading kernel from FIT Image at 7925ce00 ...
   Using 'conf' configuration
optee api revision: 2.0
find partition misc ok 
find partition security_a error 
find partition security error 
TEEC: Waring: Could not find security partition
## Verified-boot: 0
   Verifying Hash Integrity ... sha256,rsa2048:dev+ OK
   Trying 'kernel' kernel subimage
     Description:  unavailable
     Type:         Kernel Image
     Compression:  uncompressed
     Data Start:   0x7928be00
     Data Size:    39238144 Bytes = 37.4 MiB
     Architecture: AArch64
     OS:           Linux
     Load Address: 0x00280000
     Entry Point:  0x00280000
     Hash algo:    sha256
     Hash value:   6d75cfb71d410f9099cceddbb1b07a86528ba57a41f4fc67c2676e412fcf026e
   Verifying Hash Integrity ... sha256+ OK
## Loading fdt from FIT Image at 7925ce00 ...
   Using 'conf' configuration
   Trying 'fdt' fdt subimage
     Description:  unavailable
     Type:         Flat Device Tree
     Compression:  uncompressed
     Data Start:   0x7925e000
     Data Size:    187611 Bytes = 183.2 KiB
     Architecture: AArch64
     Load Address: 0x08300000
     Hash algo:    sha256
     Hash value:   acb7a59d28a3fb7893ab6a08dcad1bb17f404c0e4cca8d2fb13ed8a7614ee472
   Verifying Hash Integrity ... sha256+ OK
   Loading fdt from 0x08300000 to 0x08300000
   Booting using the fdt blob at 0x08300000
   Loading Kernel Image from 0x7928be00 to 0x00280000 ... OK
   kernel loaded at 0x00280000, end = 0x027eba00
   Using Device Tree in place at 0000000008300000, end 0000000008330cda
## reserved-memory:
  ramoops@110000: addr=110000 size=f0000
Adding bank: 0x00200000 - 0x08400000 (size: 0x08200000)
Adding bank: 0x09400000 - 0x80000000 (size: 0x76c00000)
board seed: Pseudo

......

== DO RELOCATE == Kernel from 0x00280000 to 0x00200000
Total: 882.698/929.496 ms

Starting kernel ...

将公钥的哈希值烧录到OTP区,需要设置将

# RK_SECURITY_BURN_KEY is not set修改为RK_SECURITY_BURN_KEY=y,这样第一次上电时loader阶段会自动将哈希值烧写到OTP区。

5、sdk签名流程分析

        下面将分析下SDK中是如何来对uboot和kernel进行签名的流程。主要在 check-security.sh脚本,mk-security.sh脚本分析.

         在 check-security.sh脚本中会根据配置是AVB方案还是FIT方案来检查uboot和kernel的.config配置中上面对应的配置项是否进行了配置。

#!/bin/bash -e

###################################################
RK_SCRIPTS_DIR="${RK_SCRIPTS_DIR:-$(dirname "$(realpath "$0")")}"
RK_SDK_DIR="${RK_SDK_DIR:-$RK_SCRIPTS_DIR/../../../..}"
UBOOT=$RK_SDK_DIR/u-boot
KERNEL=$RK_SDK_DIR/kernel
BUILDROOT=$RK_SDK_DIR/buildroot
RK_SIGN_TOOL=$RK_SDK_DIR/rkbin/tools/rk_sign_tool
###################################################

ROOTFS_UPDATE_ENGINEBIN_CONFIGS=" \
	BR2_PACKAGE_RECOVERY \
	BR2_PACKAGE_RECOVERY_UPDATEENGINEBIN"

ROOTFS_AB_FIXED_CONFIGS=" \
	$ROOTFS_UPDATE_ENGINEBIN_CONFIGS \
	BR2_PACKAGE_RECOVERY_BOOTCONTROL"

UBOOT_FIT_FIXED_CONFIGS=" \
	CONFIG_FIT_SIGNATURE \
	CONFIG_SPL_FIT_SIGNATURE"

UBOOT_AVB_FIXED_CONFIGS=" \
	CONFIG_ANDROID_AVB \
	CONFIG_AVB_LIBAVB \
	CONFIG_AVB_LIBAVB_AB \
	CONFIG_AVB_LIBAVB_ATX \
	CONFIG_AVB_LIBAVB_USER \
	CONFIG_RK_AVB_LIBAVB_USER \
	CONFIG_OPTEE_CLIENT \
	CONFIG_AVB_VBMETA_PUBLIC_KEY_VALIDATE \
	CONFIG_RK_AVB_LIBAVB_ENABLE_ATH_UNLOCK \
	CONFIG_OPTEE_V."

# TODO:  CONFIG_ROCKCHIP_PRELOADER_PUB_KEY

RAMBOOT_FIXED_CONFIG=" \
	BR2_PACKAGE_TEE_USER_APP \
	BR2_PACKAGE_LUKSMETA"

#检查keys是否存在
#可以使用 /build.sh createkeys 在rk3568_bsp/u-boot/keys 生成秘钥对。
rk_security_check_keys()
{
	if [ ! -d "$UBOOT/keys" ]; then
		echo "ERROR: No root keys(u-boot/keys) found in u-boot"
		echo "       Create it by ./build.sh security-createkeys or move your key to it"
		exit -1
	fi

	if echo "$1" | grep system ; then
		if [ ! -f $UBOOT/keys/root_passwd ]; then
			echo "ERROR: No root passwd(u-boot/keys/root_passwd) found in u-boot"
			echo "       echo your root key for sudo to u-boot/keys/root_passwd"
			echo "       some operations need supper user permission when create encrypt image"
			exit -1
		fi

		if [ "$1" = "system-encryption" ] && \
			[ ! -f $UBOOT/keys/system_enc_key ]; then
			echo "ERROR: No enc key(u-boot/keys/system_enc_key) found in u-boot"
			echo "       Create it by ./build.sh security-createkeys or move your key to it"
			exit -1
		fi
	fi
}

BOOT_FIXED_CONFIGS=" \
	CONFIG_BLK_DEV_DM \
	CONFIG_DM_CRYPT \
	CONFIG_DM_VERITY"

BOOT_FIXED_UNDER_6_1_CONFIG="
	CONFIG_BLK_DEV_CRYPTOLOOP"

BOOT_OPTEE_FIXED_CONFIGS=" \
	CONFIG_TEE \
	CONFIG_OPTEE"


#检查uboot或者kernel的配置文件是否有对应的配置项
config_check()
{
	# 1. config 2. match item
	echo debug-$1
	for i in $2
	do
		echo "look for $i"
		result=$(cat $1 | grep "${i}=y" -w || echo "No found")
		if [ "$result" = "No found" ]; then
			echo -e "\e[41;1;37mSecurity: No found config ${i} in $1 \e[0m"
			echo "make sure your config include this list"
			echo "---------------------------------------"
			echo "$2" | xargs -n1
			echo "---------------------------------------"
			exit -1;
		fi
	done
	return 0
}

rk_security_match_overlay()
{
	result=$(cat "$2" | grep "$3" || echo "No found")
	if [ "$result" = "No found" ]; then
		echo -e "\e[41;1;37mSecurity: No found BR2_ROOTFS_OVERLAY+=\"board/rockchip/common/$3/\" in $1 config\e[0m"
		exit -1
	fi
}

# 检查system
rk_security_check_system()
{
	case $1 in
		system-encryption|system-verity) rk_security_match_overlay system $2 security-system-overlay;;
		# base是不校验system分区
		base) return 0;;
		*) exit -1;;
	esac
}

# 检查内核配置
rk_security_check_kernel_config()
{
	# RK_SECURITY=y
	# 开启rk安全启动配置情况下才作检查
	[ ! -z "$RK_SECURITY" ] || return 0

	# 内核版本小于 6.1
	if [ $(echo "$RK_KERNEL_VERSION_RAW < 6.1" | bc) -eq 1 ]; then
		BOOT_FIXED_CONFIGS="$BOOT_FIXED_CONFIGS $BOOT_FIXED_UNDER_6_1_CONFIG"
	fi

	case $1 in
		system-encryption) BOOT_FIXED_CONFIGS="$BOOT_FIXED_CONFIGS $BOOT_OPTEE_FIXED_CONFIGS" ;& # fallthrough
		system-verity) config_check $2 "$BOOT_FIXED_CONFIGS" ;;
		base) return 0;;
		*) exit -1;;
	esac
}

#检查内核dts文件是否配置了optee 节点且enbable
rk_security_check_kernel_dts()
{
	test "$1" = "system-encryption" || return 0

	# ${ ## } 从变量开头开始匹配最长的.(点),然后删除
	if [ "${2##*.}" = "dtb" ]; then
		dtsfile=$(mktemp)
		# dtc 是 Linux 系统中用于编译或反编译设备树(Device Tree)文件的工具
		# -I:Input formats 输入文件格式
		# -O:Output formats 输出文件格式
		# -o:Output file 输出文件
		# 将输入的dtb文件反编译为dts输出到dtsfile文件中
		dtc -q -I dtb -O dts -o $dtsfile $2
	else
		dtsfile=$2
	fi

	tmp_file=$(mktemp)
	#-P:启用 Perl 兼容正则(支持更复杂的语法)。
	#-z:将文件视为单行(用 NULL 分隔),便于跨行匹配。
	#-o:仅输出匹配内容
	#\t:转义符
	#\s*{(\n|\w|-|;|=|<|>|\"|_|\s|,)*};
	#\s*:匹配0或多个空白字符(空格、制表符、换行等)
	#{:匹配左花括号。
	# (\n|\w|-|;|=|<|>|\"|_|\s|,)*:这是一个分组,包含多个选项,用|分隔,匹配任意次。这个部分意图是匹配花括号内的内容,直到遇到结束的};

	#\n:换行符,允许跨行匹配。
	#\w:单词字符(字母、数字、下划线)。
	#-:直接匹配短横线。
	#; = < > " _ \s ,:这些字符分别匹配。
	#使用*量词可能会导致贪婪匹配,直到无法继续为止,但由于后面有};作为结束,所以正则引擎会尽可能匹配到最近的};
	#};:匹配右花括号和分号,结束整个结构。

	if ! grep -Pzo "\toptee \s*{(\n|\w|-|;|=|<|>|\"|_|\s|,)*};" $dtsfile 1>$tmp_file 2>/dev/null; then
		# 匹配失败则打印提示信息
		echo -e "\e[41;1;37mNo found optee node in dts\e[0m"
		echo "Please add: "
		echo "        optee: optee {"
		echo "                compatible = \"linaro,optee-tz\";"
		echo "                method = \"smc\";"
		echo "                status = \"okay\";"
		echo "        };"
		echo "To kernel dts"

		rm -f $tmp_file
		# 如果dtsfile是使用dtc反编译出来的就将其删除
		test "$2" = "$dtsfile" || rm $dtsfile
		exit -1
	fi

	status=$(cat $tmp_file | grep -a status || true)
	if [ "$(echo $status | grep disabled)" ]; then
		rm -f $tmp_file
		test "$2" = "$dtsfile" || rm $dtsfile
		echo -e "\e[41;1;37mOptee Found, but disabled!!!\e[0m"
		exit -1
	fi

	rm -f $tmp_file
	test "$2" = "$dtsfile" || rm $dtsfile
}

# kernel内核配置检查
rk_security_check_kernel()
{
	append=$1
	shift
	case $append in
		# linux内核配置和设备树进行检查:rk_security_check_kernel_config/dts
		config|dts) rk_security_check_kernel_$append $@;;
		*) exit -1;;
	esac
}

# 检查ramboot
rk_security_check_ramboot()
{
	# 只有 RK_SECURITY_CHECK_METHOD为system-encryption情况下才作ramdisk的处理
	# 我们项目中RK_SECURITY_CHECK_METHOD="base",未使用该选项,可以不关注
	if [ "$1" != "system-encryption" ]; then
		return 0
	fi
	shift

	if [ ! -f "$1" ]; then
		echo -e "\e[41;1;37m$1 is not found\e[0m"
		exit -1
	fi

	echo "check ramdisk config"
	config_check $1 "$(echo $ROOTFS_UPDATE_ENGINEBIN_CONFIGS $RAMBOOT_FIXED_CONFIG)"
	rk_security_match_overlay ramboot $1 security-ramdisk-overlay
}

# 检查uboot
rk_security_check_uboot()
{
	METHOD=$1
	shift

	# fit配置下的uboot检查
	if [ "$METHOD" = "fit" ]; then	
		config_check $1 "$UBOOT_FIT_FIXED_CONFIGS"
	else
		# avb情况
		config_check $1 "$UBOOT_AVB_FIXED_CONFIGS"
	fi
}

rk_security_check_main()
{
	CHECK_LIST="keys kernel system uboot ramboot"

	for item in $CHECK_LIST
	do
		# $1必须在 keys kernel system uboot ramboot 参数列表中
		if [ "$item" = "$1" ]; then
			append=$1
			shift
			# 调用对应的函数rk_security_check_keys/kernel/system/uboot/ramboot
			"rk_security_check_$append" $@
		fi
	done
}

# -----------------------------------
# For SDK
# -----------------------------------

# 根据镜像来检查uboot,kernel中的配置以及设备树是否配置正确
rk_security_check_sdk()
{
	# $RK_SECURITY 非空则返回0,即如果要继续执行下去,那么$RK_SECURITY的值应该为空 
	# RK_SECURITY=y
	[ ! -z "$RK_SECURITY" ] || return 0

	# 添加打印信息发现,$1的值应该为system
	# RK_SECURITY_CHECK_METHOD="base"
	case $1 in
		keys) rk_security_check_main keys $RK_SECURITY_CHECK_METHOD ;;
		kernel)
			case $2 in
					#检查kernle 的.config文件
				config) rk_security_check_main $@ $RK_SECURITY_CHECK_METHOD $RK_SDK_DIR/kernel/.config ;;
				dts) rk_security_check_main $@ $RK_SECURITY_CHECK_METHOD $RK_KERNEL_DTB ;;
			esac
			;;
			#检查buildroot/output/.config文件
		system) rk_security_check_main system $RK_SECURITY_CHECK_METHOD $RK_SDK_DIR/buildroot/output/$RK_BUILDROOT_CFG/.config ;;
		ramboot) rk_security_check_main ramboot $RK_SECURITY_CHECK_METHOD $RK_SDK_DIR/buildroot/output/$RK_SECURITY_INITRD_CFG/.config ;;
				#检查uboot的 .config文件
		uboot) rk_security_check_main uboot $RK_SECUREBOOT_METHOD $RK_SDK_DIR/u-boot/.config ;;
	esac
}

# RK_SESSION="${RK_SESSION:-$(date +%F_%H-%M-%S)} 2025-04-15_10-58-34
# 如果 $RK_SESSION 存在且非空则执行 rk_security_check_sdk
if [ "$RK_SESSION" ]; then
	rk_security_check_sdk $@
else
	rk_security_check_main $@
fi

mk-security.sh 脚本则是根据配置是AVB方案还是FIT方案来对boot,img进行签名

#!/bin/bash -e

# TODO: Almost product have enabled bl32.
# AVB Config should be set in AVB tools dir.
#     include keys / product id / efuse

# For flash device, encryption-system remain space should be config
###################################################
RK_SCRIPTS_DIR="${RK_SCRIPTS_DIR:-$(dirname "$(realpath "$0")")}"
RK_SDK_DIR="${RK_SDK_DIR:-$RK_SCRIPTS_DIR/../../../..}"
UBOOT=$RK_SDK_DIR/u-boot
KERNEL=$RK_SDK_DIR/kernel
BUILDROOT=$RK_SDK_DIR/buildroot
RK_SIGN_TOOL=$RK_SDK_DIR/rkbin/tools/rk_sign_tool
RK_SIGN_INI=$RK_SDK_DIR/rkbin/tools/setting.ini
RK_AVB_TOOL_DIR=$RK_SDK_DIR/tools/linux/Linux_SecurityAVB/
RK_AVB_TOOL=$RK_AVB_TOOL_DIR/avb_user_tool.sh
###################################################
# 1 -> input misc	# 2 -> output misc
# 3 -> size		# 4 -> enc_key

 check_var_in_list()
{
	# 在$2中查找$1的值,成功返回0,否则返回1
	echo $2 | fgrep -wq $1 && return 0 || return 1
}

assert_var_in_list()
{
	if ! check_var_in_list "$@" ; then
		echo -e "\e[41;1;37m$1 not in List \"$2\" -- $(basename "${BASH_SOURCE[1]}") - ${FUNCNAME[1]}\e[0m"
		return 1
	fi
}

rk_security_setup_misc()
{
	SRC=$1
	DST=$2
	size=$3
	buf=$4
	echo buf=$buf

	big_end=$[size / 256]
	lit_end=$[size - (big_end * 256)]
	big_end=$(echo "ibase=10;obase=16;$big_end" | bc)
	lit_end=$(echo "ibase=10;obase=16;$lit_end" | bc)

	IMAGE_DIR="${RK_OUTDIR:-$UBOOT}/security"
	mkdir -p "$IMAGE_DIR"
	IMAGE="$IMAGE_DIR/misc-security.img"
	rm -rf "$IMAGE"

	ln -rsLf "$SRC" "$IMAGE_DIR/misc.img"
	dd if="$IMAGE_DIR/misc.img" of="$IMAGE" bs=1k count=10
	echo -en "\x$lit_end\x$big_end" >> "$IMAGE"
	echo -n "$buf" >> "$IMAGE"
	skip=$[10 * 1024 + size + 2]
	dd if="$IMAGE_DIR/misc.img" of="$IMAGE" seek=$skip skip=$skip bs=1
	ln -rsf "$IMAGE" "$DST"
}


# 使用RK_SIGN_TOOL生成签名loader,uboot的密钥。
# 因为Loader到Uboot的校验,由RK私有方案完成,所有使用rk_sign_tool工具进行签名操作。
rk_security_setup_createkeys()
{
	mkdir -p $UBOOT/keys
	cd $UBOOT/keys

	# 生成的密钥对放在uboot/keys目录下
	$RK_SIGN_TOOL kk --bits 2048 --out ./

	ln -rsf private_key.pem dev.key
	ln -rsf public_key.pem dev.pubkey
	# TODO: Some rk_sign_tool may create privateKey.pem / publicKey.pem

	# 生成签名证书
	openssl req -batch -new -x509 -key $UBOOT/keys/dev.key \
		-out $UBOOT/keys/dev.crt

	# system使用加密方式	
	if [ "$1" == "system-encryption" ]; then
		openssl rand -out $UBOOT/keys/system_enc_key -hex 32
	fi
}

rk_security_setup_system_verity()
{
	target_image=$(readlink -f $1)
	outdir=$(cd $(dirname $target_image);pwd)
	security_system=$outdir/security_system.img

	if [ -f "$outdir/security.info" ]; then
		source $outdir/security.info
		if [ "$(ls -l --time-style=long-iso $target_image | cut -d ' ' -f 6,7)" == "$touch" ]; then
			echo "security_system.img not be updated!!!"
			return 0
		fi
	fi

	sectors=$(ls -l "$target_image" | awk '{printf $5}')
	hash_offset=$[(sectors / 1024 / 1024 + 2) * 1024 * 1024]
	tmp_file=$(mktemp)
	cp "$target_image" "$security_system"
	veritysetup --hash-offset=$hash_offset format "$security_system" "$security_system" > $tmp_file

	echo "touch=\"$(ls -l --time-style=long-iso $target_image | cut -d ' ' -f 6,7)\"" > $outdir/security.info
	echo "hash_offset=$hash_offset" >> $outdir/security.info
	root_hash=$(cat $tmp_file)
	echo "root_hash=$(echo ${root_hash##*:})" >> $outdir/security.info
	# cat "$tmp_file" >> $outdir/info
	rm $tmp_file
}

rk_security_setup_system_encryption()
{
	target_image=$(readlink -f $1)
	outdir=$(cd $(dirname $target_image);pwd)
	security_system=$outdir/security_system.img

	key=$(cat $UBOOT/keys/system_enc_key)
	cipher=aes-cbc-plain

	if [ -f "$outdir/security.info" ]; then
		source $outdir/security.info
		if [ "$(ls -l --time-style=long-iso $target_image | cut -d ' ' -f 6,7)" == "$touch" ]; then
			echo "security_system.img not be updated!!!"
			return 0
		fi
	fi

	sectors=$(ls -l "$target_image" | awk '{printf $5}')
	sectors=$[(sectors + (1 * 1024 * 1024) - 1) / 512] # Align 1M / unit: 512 bytes

	loopdevice=$(losetup -f)
	mappername=encfs-$(shuf -i 1-10000000000000000000 -n 1)
	dd if=/dev/null of="$security_system" seek=$sectors bs=512

	sudo -S losetup $loopdevice "$security_system" < $UBOOT/keys/root_passwd
	sudo -S dmsetup create $mappername --table "0 $sectors crypt $cipher $key 0 $loopdevice 0 1 allow_discards" < $UBOOT/keys/root_passwd
	sudo -S dd if="$target_image" of=/dev/mapper/$mappername conv=fsync < $UBOOT/keys/root_passwd
	if sync; then
		sudo -S dmsetup remove $mappername < $UBOOT/keys/root_passwd
	fi
	sudo -S losetup -d $loopdevice < $UBOOT/keys/root_passwd

	echo "touch=\"$(ls -l --time-style=long-iso $target_image | cut -d ' ' -f 6,7)\"" > $outdir/security.info
	echo "sectors=$sectors" >> $outdir/security.info
	echo "cipher=$cipher" >> $outdir/security.info
	echo "key=$key" >> $outdir/security.info
}

rk_security_setup_system()
{
	case $1 in
		system-verity) shift; rk_security_setup_system_verity $@ ;;
		system-encryption) shift; rk_security_setup_system_encryption $@ ;;
		base) ;;
		*) exit -1;;
	esac
}

rk_security_setup_ramboot_prebuild()
{
	check_method=$1
	shift
	init_in=$1
	shift
	security_file=$1
	shift
	optee_storage=$1

	case $check_method in
		system-encryption) echo encryption ;;
		system-verity) echo verity ;;
		base) return ;;
		*) exit -1;;
	esac

	if [ ! -f "$init_in" ] || [ ! -f "$security_file" ]; then
		echo -e "\e[41;1;37minit_in or security_file is missed\e[0m"
		exit -1
	fi

	init_file="$(dirname $init_in)/init"
	cp $init_in $init_file

	if [ "$check_method" == "system-encryption" ]; then
		source "$security_file"
		sed -i "s/ENC_EN=/ENC_EN=true/" "$init_file"
		sed -i "s/CIPHER=/CIPHER=$cipher/" "$init_file"
		sed -i "s/SECURITY_STORAGE=RPMB/SECURITY_STORAGE=$optee_storage/" "$init_file"
	else
		source "$security_file"
		sed -i "s/ENC_EN=/ENC_EN=false/" "$init_file"
		sed -i "s/OFFSET=/OFFSET=$hash_offset/" "$init_file"
		sed -i "s/HASH=/HASH=$root_hash/" "$init_file"
	fi

	sed -i "s/# exec busybox switch_root/exec busybox switch_root/" "$init_file"
	echo "Generate ramdisk init for security"
}

# 配置RK_SIGN_TOOL工具,导入需要使用的密钥对,以及sign_flag配置值
rk_security_setup_sign_tool()
{
	# CHIP取$1的2~4字节内容,因为一般为rk开头,如rk3568,所以要过滤掉前面的rk字段
	CHIP=${1: 2: 4}

	${RK_SIGN_TOOL} cc --chip $CHIP
	${RK_SIGN_TOOL} lk --key $UBOOT/keys/dev.key --pubkey $UBOOT/keys/dev.pubkey

	# RK_SIGN_INI为setting.ini配置文件
	if [ "$2" != "--burn-key-hash" ]; then
		# 将查找sign_flag = 开始的行,将该行替换为sign_flag =,即删除后面的内容
		sed -i "/sign_flag=/s/.*/sign_flag=/" ${RK_SIGN_INI}
	else
		# 将查找sign_flag = 开始的行,将该行替换为sign_flag=0x20
		sed -i "/sign_flag=/s/.*/sign_flag=0x20/" ${RK_SIGN_INI}
	fi
}

# 使用RK_SIGN_TOOL 工具签名loader,uboot.img或者trust.img
rk_security_setup_uboot_avb_sign()
{
	# assert_var_in_list 查看$1是否在 "loader uboot trust"列表中
	assert_var_in_list $1 "loader uboot trust"

	# 如果 $3存在,拷贝$2到$3,因为$3为签名后的文件,所以先拷贝一份过去,避免失败后出问题
	if [ "$3" ]; then
		cp $2 $3
		DST=$3
	else
		DST=$2
	fi

	case $1 in
		loader) ${RK_SIGN_TOOL} sl --loader $DST;;
		uboot|trust) ${RK_SIGN_TOOL} si --img $DST;;
	esac
}

# AVB签名,签名boot或者recovery镜像文件
rk_security_setup_avb_sign()
{
	assert_var_in_list $1 "boot recovery"

	STAGE=$1
	# 镜像绝对路径
	SRC=$(realpath $2)
	DST_DIR=$3 

	IMAGE_DIR="${RK_OUTDIR:-$UBOOT}/security"
	mkdir -p "$IMAGE_DIR"
	IMAGE="$IMAGE_DIR/$STAGE-security.img"
	rm -rf "$IMAGE"

	cd $RK_AVB_TOOL_DIR
	$RK_AVB_TOOL -s -${STAGE} $SRC

	cp ${RK_AVB_TOOL_DIR}/out/${STAGE}.img ${IMAGE_DIR}/${STAGE}-security.img
	[ "$STAGE" != "boot" ] || \
		cp ${RK_AVB_TOOL_DIR}/out/vbmeta.img ${IMAGE_DIR}/vbmeta.img

	if [ "$DST_DIR" ]; then
		DST_DIR=$(realpath $DST_DIR)
		ln -rsf ${IMAGE} $DST_DIR/${STAGE}.img
		[ "$STAGE" != "boot" ] || \
			cp ${IMAGE_DIR}/vbmeta.img $DST_DIR/vbmeta.img
	fi

	cd -
}

# FIT方案的签名
rk_security_setup_sign()
{
	assert_var_in_list $1 "boot recovery"

	STAGE=$1
	SRC=$(realpath $2)
	DST_DIR=$3

	IMAGE_DIR="${RK_OUTDIR:-$UBOOT}/security"
	mkdir -p "$IMAGE_DIR"
	IMAGE="$IMAGE_DIR/$STAGE-security.img"
	rm -rf "$IMAGE"

	cd $UBOOT
	ln -rsLf "$SRC" "$IMAGE_DIR/$STAGE.img"
	./scripts/fit.sh --${STAGE}_img "$(realpath $IMAGE_DIR/$STAGE.img)"
	mv $STAGE.img "$IMAGE"
	ln -rsf ${IMAGE} $DST_DIR/${STAGE}.img
	cd "${RK_SDK_DIR:-..}"
}

# -----------------------------------
# For SDK
# -----------------------------------
build_security_system()
{
	[ "$RK_ROOTFS_SYSTEM_BUILDROOT" ] || warning "rootfs is not buildroot!"
	"$RK_SCRIPTS_DIR/mk-rootfs.sh"
	[ -z "$RK_SECURITY_CHECK_SYSTEM_VERITY" ] ||
		"$RK_SCRIPTS_DIR/mk-security.sh" security-ramboot

	notice "Security rootfs.img has update in output/firmware/rootfs.img"
	finish_build $@
}

build_security_ramboot()
{
	check_config RK_SECURITY_INITRD_CFG || false

	message "=========================================="
	message "          Start building security ramboot(buildroot)"
	message "=========================================="

	if [ ! -r "$RK_FIRMWARE_DIR/rootfs.img" ]; then
		notice "Rootfs is not ready, building it for security..."
		"$RK_SCRIPTS_DIR/mk-rootfs.sh"
	fi

	if [ "$RK_SECURITY_OPTEE_STORAGE_SECURITY" ]; then
		OPTEE_STORAGE=SECURITY
	else
		OPTEE_STORAGE=RPMB
	fi

	"$RK_SCRIPTS_DIR/mk-security.sh" ramboot_prebuild \
		$RK_SECURITY_CHECK_METHOD \
		$RK_SDK_DIR/buildroot/board/rockchip/common/security-ramdisk-overlay/init.in \
		$RK_OUTDIR/buildroot/images/security.info $OPTEE_STORAGE

	DST_DIR="$RK_OUTDIR/security-ramboot"
	IMAGE_DIR="$DST_DIR/images"

	"$RK_SCRIPTS_DIR/mk-buildroot.sh" $RK_SECURITY_INITRD_CFG "$IMAGE_DIR"

	if [ "$RK_USE_FIT_IMG" ]; then
		"$RK_SCRIPTS_DIR/mk-ramboot.sh" "$DST_DIR" \
			"$IMAGE_DIR/rootfs.$RK_SECURITY_INITRD_TYPE" \
			"$RK_SECURITY_FIT_ITS"
	else
		"$RK_SCRIPTS_DIR/mk-ramboot.sh" "$DST_DIR" \
			"$IMAGE_DIR/rootfs.$RK_SECURITY_INITRD_TYPE"
	fi

	"$RK_SCRIPTS_DIR/mk-security.sh" sign boot \
		$DST_DIR/ramboot.img $RK_FIRMWARE_DIR/
	notice "Security boot.img has update in output/firmware/boot.img"

	finish_build $@
}

# Hooks

BUILD_CMDS="security-createkeys security-misc security-ramboot security-system"

# sign参数后面跟 需要进行前面的镜像名称,比如sing loader
HID_CMDS="createkeys misc system ramboot_prebuild sign"


build_avb_sign()
{
	case $1 in
		loader|uboot|trust)
			# rk_security_setup_sign_tool,配置RK_SIGN_TOOL工具,导入需要使用的密钥对,以及sign_flag配置值
			# RK_SECURITY_BURN_KEY=y 在buildroot中进行配置
			rk_security_setup_sign_tool $RK_CHIP \
				"$(test $RK_SECURITY_BURN_KEY && \
					echo --burn-key-hash || \
					echo --debug-key-hash)"
			# rk_security_setup_uboot_avb_sign,使用RK_SIGN_TOOL 工具签名loader,uboot.img或者trust.img	
			# 在对uboot.img进行签名时失败,
			# Image is /home/mingl/VRC-AE05-01-gerrit/rk3568_bsp/output/firmware/uboot.img
			# the image did not support to sign
			rk_security_setup_uboot_avb_sign $@ ;;
		recovery)
			# rk_security_setup_avb_sign,AVB签名,签名boot或者recovery镜像文件
			rk_security_setup_avb_sign $@ \
				$[ $(rk_partition_size_kb recovery) * 1024 ];;
		*)
			# 默认情况走这个分支
			rk_security_setup_avb_sign $@;;
	esac
}

build_hook()
{
	case $1 in
		security-createkeys)  #使用RK_SIGN_TOOL生成签名loader,uboot的密钥。
			rk_security_setup_createkeys $RK_SECURITY_CHECK_METHOD;;
		security-misc)
			if [ "$RK_SECURITY_CHECK_SYSTEM_ENCRYPTION" ]; then
				"$RK_SCRIPTS_DIR/mk-misc.sh"
			fi
			;;
		security-ramboot) build_security_ramboot ;;
		security-system) build_security_system ;;
	esac

	# $1 参数必须是 createkeys misc system ramboot_prebuild sign内的选项,否则return 0;
	echo $HID_CMDS | fgrep "$1" -wq || return 0
	append=$1
	shift

	case $append in
		sign)
			# RK_SECUREBOOT_AVB=y 需要在buildroot中进行配置,表明开启AVB安全功能
			# 调用build_avb_sign,失败则调用rk_security_setup_$append 函数继续进行处理
			test "$RK_SECUREBOOT_AVB" && \
				build_avb_sign $@ || \
				rk_security_setup_$append $@  #rk_security_setup_sign 函数继续进行处理
			;;
		*) rk_security_setup_$append $@ ;;
	esac
}

usage_hook()
{
	echo -e "security-createkeys               \tcreate keys for security"
	echo -e "security-misc                     \tbuild misc with system encryption key"
	echo -e "security-ramboot                  \tbuild security ramboot"
	echo -e "security-system                   \tbuild security system"
}

clean_hook()
{
	rm -rf $RK_OUTDIR/security*
}

# RK_SESSION_DIR="$RK_OUTDIR/sessions"
[ -z "$RK_SESSION" ] || \
	source "${RK_BUILD_HELPER:-$(dirname "$(realpath "$0")")/../build-hooks/build-helper}"

[ -z "$1" ] || build_hook $@

 由于笔者使用的是linux,其中没有要求对只读分区的签名校验。 安卓系统的话,可能需要对system分区进行签名和校验,就需要将瑞芯微官方文档中对只读分区进行签名校验的流程完成,

你可能感兴趣的:(安全,linux)