2023年7月27日发(作者:)
rk3399-android9.0-secureboot介绍RK完整的Secureboot包括两部分,第⼀部分为Linux的Secureboot,第⼆部分为Android特有的AVB(Android Verified Boot)。开启了Secureboot的设备,会在启动时逐级校验各分区,⼀旦某⼀级校验不通过,则设备就⽆法启动。Secureboot分为安全性校验与完整性校验。安全性校验: 为公钥的校验,借助于芯⽚的⼀次性可编程安全存储模块(OTP 或 efuse), 在rk3399上称为efuse。该检验流程为从efuse中读取公钥 hash,与计算的公钥 hash 先做对⽐,如果相同,则再将公钥⽤于解密固件 hash。完整性校验: 为校验固件的完整性,计算固件的 hash 与⽤公钥解密出来的 hash 对⽐是否⼀致。AVB阶段安全性校验和完整性校验需要依赖于,相关的公钥及描述信息存储在中。Secureboot流程Secureboot涉及到的两级:maskrom —> miniloader、miniloader —> uboot、uboot—> kernel,但在Android上Secureboot部分只实现前两级,uboot—> kernel以及之后的启动校验交由AVB进⾏处理。以下以maskrom —> miniloader为例讲解Secureboot流程。pc加密过程使⽤rk提供的签名⼯具(rk_sign_tool)进⾏签名步骤及原理如下1.该⼯具⾸先会产⽣⼀对密钥对,即:public key和privete key2.使⽤SHA256计算镜像的hash,并使⽤privete key对镜像的hash进⾏RSA2048签名3.使⽤SHA256计算出public key的hash4.将镜像+第2步中签名+public key进⾏打包形成新的镜像5.第3步中的hash将会烧写到efuse中设备解密过程1.⾸先从新的镜像中获取public key计算hash值2.从efuse中读取public key的hash值进⾏对⽐,如果相同则继续,否则启动失败3.从镜像中获取签名,然后使⽤RSA2048计算hash4.使⽤SHA256计算镜像的hash值,与第三步计算出来的hash进⾏对⽐,相同则继续,否则启动失败AVB流程AVB的核⼼结构为vbmeta,vbmeta分区存储了boot分区的hash,⽽对于system和vender分区,哈希树紧随在各⾃的分区数据之后,vbmeta分区只保存哈希树描述符中哈希树的根哈希(root hash),盐(salt)和偏移量(offset)。uboot启动后,⾸先需要进⾏vbmeta的合法性验证,即安全性校验,RK的做法是将验证vbmeta的公钥信息经过trust加密后存储在security分区,其中trust分区的安全性⼜是受efuse验证的Secureboot进⾏保证的。uboot启动kernel前先验签vbmeta,vbmeta可信后,再取出vbmeta中的相关信息来进⾏其他分区的校验。Merkle Treehash listAVB在验证system分区时采⽤了动态校验的⽅式进⾏完整性校验,所以采⽤了分块进⾏hash的⽅式来校验。那么如何存储该数据块的hash,直接采⽤最暴⼒的⽅式,⾃然⽽然想到的是使⽤⼀个hash列表来存储。但是使⽤Hash列表来保证数据块的正确性还不够,⿊客修改数据的同时,如果将Hash列表也对应修改了,这就⽆法保证数据块的正确性了。所以需要引⼊⼀个顶层的hash,将hash列表⾥的每个hash字符串拼在⼀起后再做⼀次hash运算,最后的hash值称之为root hash,只要保证该root hash的正确性即可。但是AVB并未采⽤该简单结构。假设system的⼤⼩为1GB,数据块⼤⼩为4KB,则有26万个数据块,对应着hash列表就有26万个元素。AVB进⾏运⾏时校验,设备运⾏时读到哪个块就会对哪个块校验,将需要校验的块进⾏hash后更新具有26万个元素的hash列表中的⼀个元素后计算root hash,再与vbmeta中root hash作对⽐来判断数据是否正确。这个效率可想⽽知⾮常糟糕,所以AVB采⽤了⼀种称为Merkle Tree的树结构。hash treeMerkle Tree,通常也被称作Hash Tree,其叶⼦节点是数据块或者⽂件的hash值。⾮叶节点是其对应⼦节点串联字符串的hash。Hash列表可以看作⼀种特殊的Merkle Tree,即树⾼为2的多叉Merkle Tree。建树过程:在树的最底层,和hash列表⼀样,将数据分成若⼲个⼩的数据块,有相应的hash与之对应。但是往上⾛,并不是直接去计算root hash,⽽是把相邻的两个hash合并成⼀个字符串,然后计算这个字符串的hash,将这个hash值作为两个节点的⽗节点。按照同样的⽅式,可以得到数⽬更少的新⼀级hash,最终必然形成⼀棵树,树的根节点即为root hash。Merkle Tree的结构⾮常易于同步⼤⽂件或⽂件集合,按照查找树的查找思路,从root hash开始⽐对,依次往下查找到叶⼦节点即能找到需要重新同步或下载的数据块,其时间复杂度为O(logN),如果采⽤hash列表的⽅式,需要完整进⾏⼀遍遍历才能定位到不同的数据块,其时间复杂度为O(N)。Merkle Tree在数字签名、P2P⽹络、区块链等技术都有应⽤。回到本⽂介绍的AVB,AVB在运⾏时校验某⼀块时只需要更新Merkle Tree的⼀个分⽀即可计算出hash root,其运算时间⽐hash列表⼤⼤减少。在Android9上使⽤avbtool的python代码进⾏hash tree的⽣成,该算法跟上⽂描述略有不同,当1G的system进⾏4KB⼤⼩的划分,其⽣成的hash tree只有四层(包括root hash这⼀层),所以运⾏时计算hash只要沿着这个四层树的分⽀计算即可,可想⽽知效率⼤⼤提升。avbtool中建树源码分析以下分析⼀下Android9上hash tree的⽣成过程,涉及到⽤Python实现的avbtool源码的两个函数:calc_hash_level_offsets,generate_hash_treecalc_hash_level_offsetsdef calc_hash_level_offsets(image_size, block_size, digest_size): """Calculate the offsets of all the hash-levels in a Merkle-tree. Arguments: image_size: The size of the image to calculate a Merkle-tree for. block_size: The block size, e.g. 4096. digest_size: The size of each hash, e.g. 32 for SHA-256. Returns: A tuple where the first argument is an array of offsets and the second is size of the tree, in bytes. """ level_offsets = [] # ⽤来存储每⼀层在bytearray中的偏移 level_sizes = [] # 每⼀层占⽤的⼤⼩ tree_size = 0 # 树的⼤⼩ num_levels = 0 # 树的层数 # size⽤于计算时表⽰当前层的下⼀层的数据⼤⼩,从第0层(计算数据块hash)开始, # 所以初始值为image的⼤⼩ size = image_size
while size > block_size: # 计算当前层数据需要多少个块 num_blocks = (size + block_size - 1) / block_size # round_to_multiple函数⽤来将第⼀个参数舍⼊到最接近第⼆个参数的倍数 # 在这⾥就是对齐到block_size的整数倍 # 计算当前层的hash digest需要占⽤的⼤⼩ level_size = round_to_multiple(num_blocks * digest_size, block_size) level_(level_size) tree_size += level_size num_levels += 1 # 循环往上计算,所以更新size为当前层,⽤于计算上⼀层 size = level_size # 计算每⼀层在bytearray中的偏移
for n in range(0, num_levels): offset = 0 for m in range(n + 1, num_levels): offset += level_sizes[m] level_(offset) return level_offsets, tree_sizeAndroid9上将hash tree存储在bytearray中,所以需要事先计算好树的每⼀层在bytearray中的偏移,以及整个树需要多长的bytearray存储。注意,hash tree的建树过程上⾃下往上的。其实从calc_hash_level_offsets函数就可⼤致看出Android上hash tree的存储形态了,但更为形象的存储结构还是需要看generate_hash_tree函数。generate_hash_treedef generate_hash_tree(image, image_size, block_size, hash_alg_name, salt, digest_padding, hash_level_offsets, tree_size): """Generates a Merkle-tree for a file. Args: image: The image, as a file. image_size: The size of the image. block_size: The block size, e.g. 4096. hash_alg_name: The hash algorithm, e.g. 'sha256' or 'sha1'. salt: The salt to use. digest_padding: The padding for each digest. hash_level_offsets: The offsets from calc_hash_level_offsets(). tree_size: The size of the tree, in number of bytes. Returns: A tuple where the first element is the top-level hash and the second element is the hash-tree. """ hash_ret = bytearray(tree_size) hash_src_offset = 0 hash_src_size = image_size level_num = 0 while hash_src_size > block_size: level_output = '' remaining = hash_src_size while remaining > 0: hasher = (name=hash_alg_name, string=salt) # Only read from the file for the first level - for subsequent # levels, access the array we're building. # 第0层直接按照block_size读取image来进⾏hash if level_num == 0: (hash_src_offset + hash_src_size - remaining) data = (min(remaining, block_size)) # 第0层之上的每⼀层都由取其下⼀层来进⾏hash,eg: 将第m-1层的数据分块hash后⽣成m层数据 else: offset = hash_level_offsets[level_num - 1] + hash_src_size - remaining # 以block_size为单位进⾏分块 data = hash_ret[offset:offset + block_size] (data) remaining -= len(data) if len(data) < block_size: ('0' * (block_size - len(data))) level_output += () if digest_padding > 0: level_output += '0' * digest_padding padding_needed = (round_to_multiple( len(level_output), block_size) - len(level_output)) level_output += '0' * padding_needed # Copy level-output into resulting tree. offset = hash_level_offsets[level_num] hash_ret[offset:offset + len(level_output)] = level_output # Continue on to the next level. hash_src_size = len(level_output) level_num += 1 # 建树完成后,单独计算root hash hasher = (name=hash_alg_name, string=salt) (level_output) return (), hash_ret通过calc_hash_level_offsets函数计算好偏移和⼤⼩后,即可将参数传递给generate_hash_tree函数来建树了。 从建树代码的循环过程可以看出,该树的实现是将⽣成的hash拼接在⼀起作为这⼀层的数据,然后分块进⾏hash后再拼接在⼀起给到⽗层,⽽不是之前的描述MerkleTree的两两⼦节点合并后计算hash作为⽗节点。
发布者:admin,转转请注明出处:http://www.yc00.com/news/1690462968a352881.html
评论列表(0条)