当前位置: 首页 > news >正文

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方案,效果是一样的,两种方案不可混用,各个芯片具体选用那种方案,请参考产品版本。

  从上面的图中可知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进行签名。

        如果想这些配置一开始就生效,可以在 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_KEYRAMBOOT_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" ]; thenecho "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 -1fiif echo "$1" | grep system ; thenif [ ! -f $UBOOT/keys/root_passwd ]; thenecho "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 -1fiif [ "$1" = "system-encryption" ] && \[ ! -f $UBOOT/keys/system_enc_key ]; thenecho "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 -1fifi
}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 itemecho debug-$1for i in $2doecho "look for $i"result=$(cat $1 | grep "${i}=y" -w || echo "No found")if [ "$result" = "No found" ]; thenecho -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 -n1echo "---------------------------------------"exit -1;fidonereturn 0
}rk_security_match_overlay()
{result=$(cat "$2" | grep "$3" || echo "No found")if [ "$result" = "No found" ]; thenecho -e "\e[41;1;37mSecurity: No found BR2_ROOTFS_OVERLAY+=\"board/rockchip/common/$3/\" in $1 config\e[0m"exit -1fi
}# 检查system
rk_security_check_system()
{case $1 insystem-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.1if [ $(echo "$RK_KERNEL_VERSION_RAW < 6.1" | bc) -eq 1 ]; thenBOOT_FIXED_CONFIGS="$BOOT_FIXED_CONFIGS $BOOT_FIXED_UNDER_6_1_CONFIG"ficase $1 insystem-encryption) BOOT_FIXED_CONFIGS="$BOOT_FIXED_CONFIGS $BOOT_OPTEE_FIXED_CONFIGS" ;& # fallthroughsystem-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" ]; thendtsfile=$(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 $2elsedtsfile=$2fitmp_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 $dtsfileexit -1fistatus=$(cat $tmp_file | grep -a status || true)if [ "$(echo $status | grep disabled)" ]; thenrm -f $tmp_filetest "$2" = "$dtsfile" || rm $dtsfileecho -e "\e[41;1;37mOptee Found, but disabled!!!\e[0m"exit -1firm -f $tmp_filetest "$2" = "$dtsfile" || rm $dtsfile
}# kernel内核配置检查
rk_security_check_kernel()
{append=$1shiftcase $append in# linux内核配置和设备树进行检查:rk_security_check_kernel_config/dtsconfig|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" ]; thenreturn 0fishiftif [ ! -f "$1" ]; thenecho -e "\e[41;1;37m$1 is not found\e[0m"exit -1fiecho "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=$1shift# 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_LISTdo# $1必须在 keys kernel system uboot ramboot 参数列表中if [ "$item" = "$1" ]; thenappend=$1shift# 调用对应的函数rk_security_check_keys/kernel/system/uboot/ramboot"rk_security_check_$append" $@fidone
}# -----------------------------------
# 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 inkeys) 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" ]; thenrk_security_check_sdk $@
elserk_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_keycheck_var_in_list()
{# 在$2中查找$1的值,成功返回0,否则返回1echo $2 | fgrep -wq $1 && return 0 || return 1
}assert_var_in_list()
{if ! check_var_in_list "$@" ; thenecho -e "\e[41;1;37m$1 not in List \"$2\" -- $(basename "${BASH_SOURCE[1]}") - ${FUNCNAME[1]}\e[0m"return 1fi
}rk_security_setup_misc()
{SRC=$1DST=$2size=$3buf=$4echo buf=$bufbig_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=10echo -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=1ln -rsf "$IMAGE" "$DST"
}# 使用RK_SIGN_TOOL生成签名loader,uboot的密钥。
# 因为Loader到Uboot的校验,由RK私有方案完成,所有使用rk_sign_tool工具进行签名操作。
rk_security_setup_createkeys()
{mkdir -p $UBOOT/keyscd $UBOOT/keys# 生成的密钥对放在uboot/keys目录下$RK_SIGN_TOOL kk --bits 2048 --out ./ln -rsf private_key.pem dev.keyln -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" ]; thenopenssl rand -out $UBOOT/keys/system_enc_key -hex 32fi
}rk_security_setup_system_verity()
{target_image=$(readlink -f $1)outdir=$(cd $(dirname $target_image);pwd)security_system=$outdir/security_system.imgif [ -f "$outdir/security.info" ]; thensource $outdir/security.infoif [ "$(ls -l --time-style=long-iso $target_image | cut -d ' ' -f 6,7)" == "$touch" ]; thenecho "security_system.img not be updated!!!"return 0fifisectors=$(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_fileecho "touch=\"$(ls -l --time-style=long-iso $target_image | cut -d ' ' -f 6,7)\"" > $outdir/security.infoecho "hash_offset=$hash_offset" >> $outdir/security.inforoot_hash=$(cat $tmp_file)echo "root_hash=$(echo ${root_hash##*:})" >> $outdir/security.info# cat "$tmp_file" >> $outdir/inform $tmp_file
}rk_security_setup_system_encryption()
{target_image=$(readlink -f $1)outdir=$(cd $(dirname $target_image);pwd)security_system=$outdir/security_system.imgkey=$(cat $UBOOT/keys/system_enc_key)cipher=aes-cbc-plainif [ -f "$outdir/security.info" ]; thensource $outdir/security.infoif [ "$(ls -l --time-style=long-iso $target_image | cut -d ' ' -f 6,7)" == "$touch" ]; thenecho "security_system.img not be updated!!!"return 0fifisectors=$(ls -l "$target_image" | awk '{printf $5}')sectors=$[(sectors + (1 * 1024 * 1024) - 1) / 512] # Align 1M / unit: 512 bytesloopdevice=$(losetup -f)mappername=encfs-$(shuf -i 1-10000000000000000000 -n 1)dd if=/dev/null of="$security_system" seek=$sectors bs=512sudo -S losetup $loopdevice "$security_system" < $UBOOT/keys/root_passwdsudo -S dmsetup create $mappername --table "0 $sectors crypt $cipher $key 0 $loopdevice 0 1 allow_discards" < $UBOOT/keys/root_passwdsudo -S dd if="$target_image" of=/dev/mapper/$mappername conv=fsync < $UBOOT/keys/root_passwdif sync; thensudo -S dmsetup remove $mappername < $UBOOT/keys/root_passwdfisudo -S losetup -d $loopdevice < $UBOOT/keys/root_passwdecho "touch=\"$(ls -l --time-style=long-iso $target_image | cut -d ' ' -f 6,7)\"" > $outdir/security.infoecho "sectors=$sectors" >> $outdir/security.infoecho "cipher=$cipher" >> $outdir/security.infoecho "key=$key" >> $outdir/security.info
}rk_security_setup_system()
{case $1 insystem-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=$1shiftinit_in=$1shiftsecurity_file=$1shiftoptee_storage=$1case $check_method insystem-encryption) echo encryption ;;system-verity) echo verity ;;base) return ;;*) exit -1;;esacif [ ! -f "$init_in" ] || [ ! -f "$security_file" ]; thenecho -e "\e[41;1;37minit_in or security_file is missed\e[0m"exit -1fiinit_file="$(dirname $init_in)/init"cp $init_in $init_fileif [ "$check_method" == "system-encryption" ]; thensource "$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"elsesource "$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"fised -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=0x20sed -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" ]; thencp $2 $3DST=$3elseDST=$2ficase $1 inloader) ${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} $SRCcp ${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.imgif [ "$DST_DIR" ]; thenDST_DIR=$(realpath $DST_DIR)ln -rsf ${IMAGE} $DST_DIR/${STAGE}.img[ "$STAGE" != "boot" ] || \cp ${IMAGE_DIR}/vbmeta.img $DST_DIR/vbmeta.imgficd -
}# FIT方案的签名
rk_security_setup_sign()
{assert_var_in_list $1 "boot recovery"STAGE=$1SRC=$(realpath $2)DST_DIR=$3IMAGE_DIR="${RK_OUTDIR:-$UBOOT}/security"mkdir -p "$IMAGE_DIR"IMAGE="$IMAGE_DIR/$STAGE-security.img"rm -rf "$IMAGE"cd $UBOOTln -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}.imgcd "${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-rambootnotice "Security rootfs.img has update in output/firmware/rootfs.img"finish_build $@
}build_security_ramboot()
{check_config RK_SECURITY_INITRD_CFG || falsemessage "=========================================="message "          Start building security ramboot(buildroot)"message "=========================================="if [ ! -r "$RK_FIRMWARE_DIR/rootfs.img" ]; thennotice "Rootfs is not ready, building it for security...""$RK_SCRIPTS_DIR/mk-rootfs.sh"fiif [ "$RK_SECURITY_OPTEE_STORAGE_SECURITY" ]; thenOPTEE_STORAGE=SECURITYelseOPTEE_STORAGE=RPMBfi"$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_STORAGEDST_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 $@
}# HooksBUILD_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 inloader|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 signrk_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 insecurity-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 0append=$1shiftcase $append insign)# 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分区进行签名和校验,就需要将瑞芯微官方文档中对只读分区进行签名校验的流程完成,

相关文章:

rk3568安全启动功能实践

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

transformer-实现解码器Decoder

Decoder 论文地址 https://arxiv.org/pdf/1706.03762 Decoder结构介绍 Transformer Decoder是Transformer模型的核心生成组件&#xff0c;负责基于编码器输出和已生成内容预测后续token。通过堆叠多层结构相同的解码层&#xff08;Decoder Layer&#xff09;&#xff0c;每层包…...

iOS RunLoop 深入解析

本文深入探讨 iOS 中 RunLoop 的实现原理、工作机制以及实际应用。通过源码分析和实际案例&#xff0c;帮助读者全面理解 RunLoop 在 iOS 系统中的重要作用。 一、RunLoop 基础概念 1. RunLoop 的定义与作用 RunLoop 是 iOS 系统中用于处理事件和消息的循环机制。它负责管理线…...

软考中级-软件设计师 数据结构(手写笔记)

第一章&#xff1a;基础 基础知识 五大特性 第二章&#xff1a;线性表 第三章&#xff1a;栈和队列 队列 广义表 第四章&#xff1a;树和二叉树 基础知识 树转二叉树和二叉排序树 哈夫曼树 线索二叉树和平衡二叉树 第五章&#xff1a;图 基础知识和邻接矩阵和邻接表 图的遍…...

/var/log/sssd/` 目录解析

/var/log/sssd/ 是 System Security Services Daemon (SSSD) 的专用日志目录,用于记录与身份认证、用户/组信息查询、缓存管理等相关的操作。以下是该目录的详细解析: 1. 目录结构 默认情况下,/var/log/sssd/ 包含以下日志文件: /var/log/sssd/ ├── sssd.log …...

C++负载均衡远程调用学习之Reactor事件触发机制

目录 1.LARV0.2-REACTOR_BUF实现 2.LARV0.2-outpu_buf实现 3.LARV0.2-reactor继承内存管理 4.LARV0.2流程总结 5.LARV0.3-多路IO事件的分析 6.LARV0.3_io_event和event_loop定义 7.LARV0.3_event_loop添加一个事件 8.LARV0.3_event_loop的epoll_wait封装 9.LARV0.3-eve…...

将uni-app前端项目发布到微信小程序体验版

1、修改后端接口调用地址 const REQUEST_CONST {BASE_URL:https://11.22.33.44:9090, } export default REQUEST_CONST 2、登录微信小程序平台&#xff0c;获取AppID 3、配置微信小程序AppID 在项目根目录下找到manifest.json文件&#xff0c;配置微信小程序相关的参数 4、…...

深入理解CSS显示模式与盒子模型

一、CSS显示模式&#xff1a;元素的“性格”决定布局 1. 显示模式基础 CSS显示模式&#xff08;display属性&#xff09;决定了元素在页面中的排列方式和尺寸表现。常见的显示模式有三大类型&#xff1a; 2. 块级元素&#xff08;Block&#xff09; 特点&#xff1a;独占一…...

突破SQL注入字符转义的实战指南:绕过技巧与防御策略

在渗透测试中&#xff0c;SQL注入始终是Web安全的重点攻击手段。然而&#xff0c;当开发者对用户输入的特殊字符&#xff08;如单引号、反斜杠&#xff09;进行转义时&#xff0c;传统的注入方式往往会失效。本文将深入探讨如何绕过字符转义限制&#xff0c;并给出防御建议。 目…...

java网络原理5

一、网络地址转换&#xff08;NAT&#xff09; 1. 原理 - NAT 用于解决 IP 地址不够用的问题 &#xff0c;将 IP 地址分为外网 IP&#xff08;公网 IP&#xff09;和内网 IP&#xff08;私网 IP&#xff09;。内网 IP 如 10.、172.16 - 172.31.、192.168.* 等&#xff0c;家用…...

一种基于光源评估并加权平均的自动白平衡方法(一)

在之前的博文如何在白平衡标定种构建不同类型的白平衡色温坐标系作为实例说明的白平衡色温坐标系的构建中,利用的如下映射矩阵构建色温坐标系: 按照上述论文的说明,是不能直接把Raw域中的每块的RGB带入公式...

基于Docker Compose的Prometheus监控系统一键部署方案

前言 在当今的云原生时代,系统监控已经成为保障业务稳定运行的重要基石。本文旨在提供一个完整的解决方案,帮助您快速搭建一个功能强大的监控系统。通过Docker Compose实现一键部署,结合Prometheus、Grafana、cAdvisor和node-exporter等优秀开源工具,构建一个完整的监控体…...

服务器丢包率测试保姆级教程:从Ping到网络打流仪实战

测试服务器丢包率是网络性能诊断的重要环节&#xff0c;丢包通常由网络拥塞、硬件故障、配置错误或线路质量差导致。以下是多种测试方法的详细步骤和工具说明&#xff1a; 一、基础工具测试&#xff08;无需专业设备&#xff09; 1. 使用 ping 命令 命令示例&#xff1a; bash…...

家庭服务器IPV6搭建无限邮箱系统指南

qq邮箱操作 // 邮箱配置信息 // 注意&#xff1a;使用QQ邮箱需要先开启IMAP服务并获取授权码 // 设置方法&#xff1a;登录QQ邮箱 -> 设置 -> 账户 -> 开启IMAP/SMTP服务 -> 生成授权码 服务器操作 fetchmail 同步QQ邮箱 nginx搭建web显示本地同步过来的邮箱 ssh…...

Ubuntu ZLMediakit的标准配置文件(rtsp->rtmp->hls)

最近在工作中遇到不生成hls资源的问题,后面发现是配置文件有误,特此记录正确的config.ini配置文件,方便查阅。 最终解决方案,通过下面这种格式可以访问到flv视频,具体为什么不太清楚,rtmp格式:rtmp://39.113.48.113:8089/live/1744168516937396175 记录最终解决方案:ht…...

Android 移动开发:ProgressBar(转圈进度条)

目录 Android 移动开发&#xff1a;ProgressBar&#xff08;转圈进度条&#xff09;控件实战介绍 &#x1f4c2; 文件说明 &#x1f9fe; activity_main.xml&#xff08;布局文件&#xff0c;XML&#xff09; &#x1f9fe; MainActivity.java&#xff08;逻辑代码&#xf…...

CSS:选择器-复合选择器

文章目录 1、交集选择器 1、交集选择器 <style>/* 选中类名为rich的元素*/.rich {color: gold;}/* 选中类名为beauty的元素*/.beauty {color: red;}/* 选中类名为beauty的p元素&#xff0c;这种形式&#xff08;元素配合类选择器&#xff09;以后用的很多&#xff01;&am…...

Kafka-可视化工具-Offset Explorer

安装&#xff1a; 下载地址&#xff1a;Offset Explorer 安装好后如图&#xff1a; 1、下载安装完毕&#xff0c;进行新增连接&#xff0c;启动offsetexplorer.exe&#xff0c;在Add Cluster窗口Properties 选项下填写Cluster name 和 kafka Cluster Version Cluster name (集…...

在pycharm中创建Django项目并启动

Django介绍 Django 是一个基于 Python 的开源 Web 应用框架&#xff0c;采用了 MTV&#xff08;Model - Template - View&#xff09;软件设计模式 &#xff0c;由许多功能强大的组件组成&#xff0c;能够帮助开发者快速、高效地创建复杂的数据库驱动的 Web 应用程序。它具有以…...

私有知识库 Coco AI 实战(六):打造 ES Mapping 小助手

开发同学可能经常和字段类型打交道&#xff0c;数据类型本来就不少&#xff0c;新版本可能还有新的数据类型。更重要的是新的字段类型可能会提升某个场景的性能&#xff0c;不知道的话可就亏大发了。所以我们继续打造一个 ES Mapping 小助手。 克隆小助手 我们进入 Coco Serv…...

JavaScript性能优化实战之代码层面性能优化

在前端开发中,JavaScript 的性能直接影响到网站的加载速度、用户体验和交互流畅度。针对代码层面的优化,我们可以从多个方面入手,确保每一行代码都能最大化地发挥效能。接下来,我们将细化并解释每一个优化点。 1️⃣ 避免全局变量污染 全局变量会被整个 JavaScript 代码所…...

基于C++的IOT网关和平台2:github项目ctGateway技术说明书

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。 源码指引:github源码指引_初级代码游戏的博客-CSDN博客 …...

前端基础之《Vue(13)—重要API》

重要的API 一、nextTick() 1、写法 Vue.$nextTick()或者this.$nextTick() 原因&#xff1a; set操作代码是同步的&#xff0c;但是代码背后的行为是异步的。set操作修改声明式变量&#xff0c;触发re-render生成新的虚拟DOM&#xff0c;进一步执行diff运算&#xff0c;找到…...

Python爬虫实战:获取彼岸网高清素材图片

一、引言 在数字化时代,图片素材的需求持续增长。彼岸网提供了丰富的高质量图片资源,其中 4K 风景图片备受用户青睐。借助 Python 爬虫技术,可自动化地从彼岸网获取这些图片,为用户提供便捷的图片素材服务。然而,爬取过程中会遭遇登录验证、反爬机制等问题,需采用相应技…...

拥抱 Kotlin Flow

1. 引言 Kotlin Flow 是 Kotlin 协程生态中处理异步数据流的核心工具&#xff0c;它提供了一种声明式、轻量级且与协程深度集成的响应式编程模型。与传统的 RxJava 相比&#xff0c;Flow 更简洁、更易于维护&#xff0c;尤其在 Android 开发中已成为主流选择。本文将从基础概念…...

winget使用

Get-Command winget winget search qq winget install Tencent.QQ.NT...

C++从入门到实战(十一)详细讲解C/C++语言中内存分布与C与C++内存管理对比

C从入门到实战&#xff08;十一&#xff09;详细讲解C/C语言中内存分布与C与C内存管理对比 前言一、C/C语言中内存分布1.内核空间2.栈3.堆4.数据段5.代码段 二、例题带练巩固C/C语言中内存分布的知识题目讲解题目答案 三、C语言动态内存分配&#xff08;知识回顾&#xff09;3.…...

flutter 专题 一百零四 Flutter环境搭建

Flutter简介 Flutter 是Google开发的一个移动跨平台&#xff08;Android 和 iOS&#xff09;的开发框架&#xff0c;使用的是 Dart 语言。和 React Native 不同的是&#xff0c;Flutter 框架并不是一个严格意义上的原生应用开发框架。Flutter 的目标是用来创建高性能、高稳定性…...

傅里叶与相位偏移

一、简介 大三的《离散数学》。。。。。 傅里叶变换是数学与工程领域的一项革命性工具&#xff0c;其核心思想是将复杂信号分解为简单正弦波的叠加&#xff0c;实现从时域&#xff08;时间维度&#xff09;到频域&#xff08;频率维度&#xff09;的转换。通过这种变换&#x…...

Godot笔记:入门索引

文章目录 前言游戏引擎软件界面关键概念GDScript导出成品创建非游戏应用后记 前言 最近对游戏引擎这块感兴趣&#xff0c;特别是因为游戏引擎自带的很多工具&#xff0c;作为图形化软件的开发应该也不错。 Godot 是一款这几年比较流行的开源游戏引擎。这里记录下入门学习使用 …...

OpenCV实战教程 第一部分:基础入门

第一部分&#xff1a;基础入门 1. OpenCV简介 什么是OpenCV及其应用领域 OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开源的计算机视觉和机器学习软件库&#xff0c;于1999年由Intel公司发起&#xff0c;现在由非营利组织OpenCV.org维护。Ope…...

OpenCV 图像处理核心技术 (第二部分)

欢迎来到 OpenCV 图像处理的第二部分&#xff01;在第一部分&#xff0c;我们学习了如何加载、显示、保存图像以及访问像素等基础知识。现在&#xff0c;我们将深入探索如何利用 OpenCV 提供的强大工具来修改和分析图像。 图像处理是计算机视觉领域的基石。通过对图像进行各种…...

Git从入门到精通-第二章-工具配置

目录 命令行 安装Git 初次运行Git前的配置​ git config基本概念 常用命令 配置用户信息 配置文本编辑器 查看配置 配置别名&#xff08;简化命令&#xff09; 高级配置 换行符处理&#xff08;方便跨平台协作&#xff09; 忽略文件权限变更&#xff08;常用于团队协…...

树状结构转换工具类

项目中使用了很多树状结构&#xff0c;为了方便使用开发一个通用的工具类。 使用工具类的时候写一个类基础BaseNode&#xff0c;如果有个性化字段添加到类里面&#xff0c;然后就可以套用工具类。 工具类会将id和pid做关联返回一个树状结构的集合。 使用了hutool的工具包判空…...

C#基础简述

C#基础详解 一、C#语言概述 C#&#xff08;读作"C Sharp"&#xff09;是微软开发的面向对象的编程语言&#xff0c;运行在.NET平台上。它结合了C的强大功能和Visual Basic的简单性&#xff0c;具有以下特点&#xff1a; ​​面向对象​​&#xff1a;支持封装、继…...

AI赋能烟草工艺革命:虫情监测步入智能化时代

在当今竞争激烈且品质至上的烟草行业中&#xff0c;生产流程的每一个细微环节都关乎着企业的生死存亡与品牌的兴衰荣辱。烟草工艺部门与制丝、卷包车间作为生产链条的核心驱动&#xff0c;犹如精密仪器中的关键齿轮&#xff0c;彼此紧密咬合、协同运转&#xff0c;任何一处的小…...

小刚说C语言刷题—1462小明的游泳时间

1.题目描述 伦敦奥运会要到了&#xff0c;小明在拼命练习游泳准备参加游泳比赛。 这一天&#xff0c;小明给自己的游泳时间做了精确的计时&#xff08;本题中的计时都按 24 小时制计算&#xff09;&#xff0c;它发现自己从 a 时 b 分一直游泳到当天的 c 时 d 分。 请你帮小…...

StarRocks Lakehouse 如何重构大数据架构?

随着数据分析需求的不断演进&#xff0c;企业对数据处理架构的期望也在不断提升。在这一背景下&#xff0c;StarRocks 凭借其高性能的实时分析能力&#xff0c;正引领数据分析进入湖仓一体的新时代。 4 月 18 日&#xff0c;镜舟科技高级技术专家单菁茹做客开源中国直播栏目《…...

用TCP实现服务器与客户端的交互

引言&#xff1a; 这篇文章主要是用TCP构造的回显服务器&#xff0c;也就是客户端发什么&#xff0c;就返回什么。用实现这个过程方式来学会TCP套接字的使用。 一、TCP的特点 TCP是可靠的&#xff1a;这个需要去了解TCP的机制&#xff0c;这是一个大工程&#xff0c;博主后面写…...

用于实时辐射场渲染的3D高斯溅射——3D Gaussian Splatting for Real-Time Radiance Field Rendering

用于实时辐射场渲染的3D高斯溅射——3D Gaussian Splatting for Real-Time Radiance Field Rendering 文章目录 用于实时辐射场渲染的3D高斯溅射——3D Gaussian Splatting for Real-Time Radiance Field Rendering摘要Abstract1. 预备知识1.1 三维的几何表示1.2 计算机中的集合…...

Vue3 Echarts 3D立方体柱状图实现教程

文章目录 前言一、实现原理二、series ——type: "pictorialBar" 简介2.1 常用属性 三、代码实战3.1 封装一个echarts通用组件 echarts.vue3.2 实现一个立方体柱状图&#xff08;1&#xff09;首先实现一个基础柱状图&#xff08;2&#xff09;添加立方体棱线&#x…...

Soildworks怎样在装配体中建立局部剖视图

1思路&#xff1a;建立拉伸切除 2步骤 1-打开点线面显示按钮 2-在装配体中依据某个基准面&#xff08;例如前视基准面&#xff09;建立一个待切除的草图 3-点击顶部工具栏的装配体--->装嫩配体特征---->拉伸切除---Ok 3具体图示 1-点击&#xff0c;使其变成灰色 即…...

基于C++的IOT网关和平台5:github项目ctGateway开发指南

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。 源码指引:github源码指引_初级代码游戏的博客-CSDN博客 …...

虚拟机centos7安装docker

虚拟机CentOS 7上安装 Docker流程 1. 更新系统软件包 需要确保系统软件包是最新的 sudo yum -y update sudo&#xff1a;以超级用户权限执行命令。 yum&#xff1a;CentOS的包管理器工具。 -y&#xff1a;自动确认所有提示&#xff0c;直接执行。 2. 安装 Docker 依赖 在安装 …...

11.Spring Boot 3.1.5 中使用 SpringDoc OpenAPI(替代 Swagger)生成 API 文档

Spring Boot 3.1.5 中使用 SpringDoc OpenAPI&#xff08;替代 Swagger&#xff09;生成 API 文档 1. 项目结构 假设项目名为 springboot-openapi-demo&#xff0c;以下是项目的基本结构&#xff1a; springboot-openapi-demo/ ├── src/ │ ├── main/ │ │ ├─…...

pytorch对应gpu版本是否可用判断逻辑

# gpu_is_ok.py import torchdef check_torch_gpu():# 打印PyTorch版本print(f"PyTorch version: {torch.__version__}")# 检查CUDA是否可用cuda_available torch.cuda.is_available()print(f"CUDA available: {cuda_available}")if cuda_available:# 打印…...

Kubernetes 集群概念详解

Kubernetes 集群概念详解 Kubernetes 集群是由多个计算节点组成的容器编排系统&#xff0c;用于自动化部署、扩展和管理容器化应用。以下是 Kubernetes 集群的核心概念和架构解析&#xff1a; 一、集群基础架构 1. 集群组成要素 graph TBMaster[控制平面] --> Node1[工作…...

BT137-ASEMI机器人功率器件专用BT137

编辑&#xff1a;LL BT137-ASEMI机器人功率器件专用BT137 型号&#xff1a;BT137 品牌&#xff1a;ASEMI 封装&#xff1a;TO-220F 批号&#xff1a;最新 引脚数量&#xff1a;3 封装尺寸&#xff1a;如图 特性&#xff1a;双向可控硅 工作结温&#xff1a;-40℃~150℃…...

ArcGIS+GPT:多领域地理分析与决策新方案

技术点目录 AI大模型应用ArcGIS工作流程及功能prompt的使用技巧AI助力工作流程AI助力数据读取AI助力数据编辑与处理AI助力空间分析AI助力遥感分析AI助力二次开发AI助力科研绘图ArcGISAI综合应用了解更多 ——————————————————————————————————…...

鸿蒙文件上传-从前端到后端详解,对比jq请求和鸿蒙arkts请求区别,对比new FormData()和鸿蒙arktsrequest.uploadFile

需要权限&#xff1a;ohos.permission.INTERNET 1.nodejs自定义书写上传后端接口 传输过来的数据放在files?.image下 router.post(/upload,(req, res) > {var form new multiparty.Form();form.uploadDirpublic/images/uploads; //上传图片保存的地址(目录必须存在)fo…...