前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >rk3399-android9.0-secureboot介绍

rk3399-android9.0-secureboot介绍

作者头像
菜菜cc
发布于 2022-11-15 13:31:02
发布于 2022-11-15 13:31:02
2.7K00
代码可运行
举报
运行总次数:0
代码可运行

RK完整的Secureboot包括两部分,第一部分为Linux的Secureboot,第二部分为Android特有的AVB(Android Verified Boot)。开启了Secureboot的设备,会在启动时逐级校验各分区,一旦某一级校验不通过,则设备就无法启动。

Secureboot分为安全性校验与完整性校验。

  • 安全性校验: 为公钥的校验,借助于芯片的一次性可编程安全存储模块(OTP 或 efuse), 在rk3399上称为efuse。该检验流程为从efuse中读取公钥 hash,与计算的公钥 hash 先做对比,如果相同,则再将公钥用于解密固件 hash。
  • 完整性校验: 为校验固件的完整性,计算固件的 hash 与用公钥解密出来的 hash 对比是否一致。

AVB阶段安全性校验和完整性校验需要依赖于vbmeta.img,相关的公钥及描述信息存储在vbmeta.img中。

Secureboot流程

Secureboot涉及到的两级:maskrom —> miniloader、miniloader —> uboot、uboot—> kernel,但在Android上Secureboot部分只实现前两级,uboot—> kernel以及之后的启动校验交由AVB进行处理。以下以maskrom —> miniloader为例讲解Secureboot流程。

pc加密过程

(adsbygoogle=window.adsbygoogle||[]).push({})

使用rk提供的签名工具(rk_sign_tool)进行签名步骤及原理如下

1.该工具首先会产生一对密钥对,即:public key和privete key

2.使用SHA256计算镜像的hash,并使用privete key对镜像的hash进行RSA2048签名

3.使用SHA256计算出public key的hash

4.将镜像+第2步中签名+public key进行打包形成新的镜像

5.第3步中的hash将会烧写到efuse中

设备解密过程

1.首先从新的镜像中获取public key计算hash值

2.从efuse中读取public key的hash值进行对比,如果相同则继续,否则启动失败

3.从镜像中获取签名,然后使用RSA2048计算hash

4.使用SHA256计算镜像的hash值,与第三步计算出来的hash进行对比,相同则继续,否则启动失败

AVB流程

AVB的核心结构为vbmeta,vbmeta分区存储了boot分区的hash,而对于systemvender分区,哈希树紧随在各自的分区数据之后,vbmeta分区只保存哈希树描述符中哈希树的根哈希(root hash),盐(salt)和偏移量(offset)。

uboot启动后,首先需要进行vbmeta的合法性验证,即安全性校验,RK的做法是将验证vbmeta的公钥信息经过trust加密后存储在security分区,其中trust分区的安全性又是受efuse验证的Secureboot进行保证的。uboot启动kernel前先验签vbmeta,vbmeta可信后,再取出vbmeta中的相关信息来进行其他分区的校验。

Merkle Tree

hash list

AVB在验证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 tree

Merkle 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_offsetsgenerate_hash_tree

calc_hash_level_offsets

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def 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_sizes.append(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_offsets.append(offset)

  return level_offsets, tree_size

Android9上将hash tree存储在bytearray中,所以需要事先计算好树的每一层在bytearray中的偏移,以及整个树需要多长的bytearray存储。注意,hash tree的建树过程上自下往上的。其实从calc_hash_level_offsets函数就可大致看出Android上hash tree的存储形态了,但更为形象的存储结构还是需要看generate_hash_tree函数。

generate_hash_tree

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def 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 = hashlib.new(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:
        image.seek(hash_src_offset + hash_src_size - remaining)
        data = image.read(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]
      hasher.update(data)

      remaining -= len(data)
      if len(data) < block_size:
        hasher.update('\0' * (block_size - len(data)))
      level_output += hasher.digest()
      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 = hashlib.new(name=hash_alg_name, string=salt)
  hasher.update(level_output)
  return hasher.digest(), hash_ret

通过calc_hash_level_offsets函数计算好偏移和大小后,即可将参数传递给generate_hash_tree函数来建树了。 从建树代码的循环过程可以看出,该树的实现是将生成的hash拼接在一起作为这一层的数据,然后分块进行hash后再拼接在一起给到父层,而不是之前的描述Merkle Tree的两两子节点合并后计算hash作为父节点。

本文作者: Ifan Tsai  (菜菜)

本文链接: https://cloud.tencent.com/developer/article/2164593

版权声明: 本文采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-01-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Android 系统架构及HAL层概述
apex_payload.img是由dm-verity支持的ext4文件系统映像。各种原生常规文件包含在apex_payload.img文件中
wizzie
2022/09/28
11.8K0
Android 系统架构及HAL层概述
eos源码赏析(二十三):默克尔树在EOS中的应用(上)
前面文章中在分析push_transactioneos源码赏析(二十):EOS智能合约之push_transaction的天龙八“步”以及区块签名eos源码赏析(二十一):EOS智能合约之区块签名的天龙八“步”的时候都提到了默克尔树,受限于篇幅未做具体分析。今天我们来谈谈默克尔树在eos中的应用。拟分为上下两篇,上篇主要分为以下内容:
用户2569546
2021/11/23
6710
[源码解析] NVIDIA HugeCTR,GPU 版本参数服务器---(7) ---Distributed Hash之前向传播
在这系列文章中,我们介绍了 HugeCTR,这是一个面向行业的推荐系统训练框架,针对具有模型并行嵌入和数据并行密集网络的大规模 CTR 模型进行了优化。
罗西的思考
2022/05/09
1.3K0
[源码解析] NVIDIA HugeCTR,GPU 版本参数服务器---(7) ---Distributed Hash之前向传播
浅谈 Android 的安全启动和完整性保护
在 IoT 中保证设备安全性的重要一环就是保证代码的完整性,不让恶意代码影响业务的正常逻辑。一般而言是及时修复现有攻击面所面临的漏洞,比如浏览器、蓝牙、调试接口;另一方面需要确保的是即便恶意代码获取了执行权限,也无法修改系统镜像进行持久化。针对这点所构造的安全方案通常称为 Secure Boot,对于不同的厂商,实现上可能会引入不同的名字,比如 Verified Boot、High Assurance Boot 等等,但本质上都是类似的。
evilpan
2023/02/12
4K0
浅谈 Android 的安全启动和完整性保护
AB升级(5):payload结构解析
payload随着AB分区和update engine引入,其中payload的数据定义在
小胡子
2022/05/30
3.5K1
014 | 从微观到宏观理解区块链
原创文章,转载请注明:转载自Keegan小钢并标明原文链接:http://keeganlee.me/post/blockchain/20180224微信订阅号:keeganlee_me写于2018-02-24
Keegan小钢
2018/08/10
7430
014 | 从微观到宏观理解区块链
【图文详解】一文全面彻底搞懂HBase、LevelDB、RocksDB等NoSQL背后的存储原理:LSM-tree 日志结构合并树
LSM 树广泛用于数据存储,例如 RocksDB、Apache AsterixDB、Bigtable、HBase、LevelDB、Apache Accumulo、SQLite4、Tarantool、WiredTiger、Apache Cassandra、InfluxDB和ScyllaDB等。
一个会写诗的程序员
2022/11/30
3.6K0
【图文详解】一文全面彻底搞懂HBase、LevelDB、RocksDB等NoSQL背后的存储原理:LSM-tree 日志结构合并树
文件系统专栏 | 之ext4文件系统结构
上次讲了VFS层,这次说说文件系统层,文件系统层将不同的文件系统实现了VFS的这些函数,通过指针注册到VFS里面。所以,用户的操作通过VFS转到各种文件系统,linux用到最多的是ext4文件系统,我们就说这个吧。EXT4是第四代扩展文件系统(英语:Fourth extended filesystem,缩写为 ext4)是Linux系统下的日志文件系统,是ext2和ext3文件系统的后继版本。 ext4文件系统布局 一个Ext4文件系统被分成一系列块组。为减少磁盘碎片产生的性能瓶颈,块分配器尽量保持每个文件
刘盼
2022/08/29
3.7K0
文件系统专栏 | 之ext4文件系统结构
从青铜到王者系列:跟着文件系统学数据结构
"走暗路、耕瘦田、进窄门、见微光"告诉我,面试关键就在于深入理解自己的项目,这才是最考察基本功的地方。然而现实情况往往是这样的:
早起的鸟儿有虫吃
2025/05/09
640
从青铜到王者系列:跟着文件系统学数据结构
[源码解析] NVIDIA HugeCTR,GPU版本参数服务器--- (6) --- Distributed hash表
在这系列文章中,我们介绍了 HugeCTR,这是一个面向行业的推荐系统训练框架,针对具有模型并行嵌入和数据并行密集网络的大规模 CTR 模型进行了优化。
罗西的思考
2022/05/09
7730
[源码解析] NVIDIA HugeCTR,GPU版本参数服务器--- (6) --- Distributed hash表
[源码解析] NVIDIA HugeCTR,GPU 版本参数服务器 --(9)--- Local hash表
在这个系列中,我们介绍了 HugeCTR,这是一个面向行业的推荐系统训练框架,针对具有模型并行嵌入和数据并行密集网络的大规模 CTR 模型进行了优化。本文介绍 LocalizedSlotSparseEmbeddingHash 的后向操作。
罗西的思考
2022/05/09
8600
[源码解析] NVIDIA HugeCTR,GPU 版本参数服务器 --(9)--- Local hash表
你真的理解Faster RCNN吗?捋一捋Pytorch官方Faster RCNN代码
来源丨https://zhuanlan.zhihu.com/p/145842317
3D视觉工坊
2020/11/11
1.9K0
你真的理解Faster RCNN吗?捋一捋Pytorch官方Faster RCNN代码
比特币核心概念及算法
bitcoin项目地址位于github仓库,当前各种“币”,基本都是从抄写bitcoin代码开始起步的。想要深度研究,从看源码开始不可避免。 P2P:电骡、迅雷、BT,在中国网络影视的发展让大家对P2P很熟悉,可能已经没有人记得比特币实际上是第一批P2P的实践者。所有交易记录在全网通过P2P的方式广播,每个人都保存一份完整的交易记录。所以也叫去中心化。 去中心化:bitcoin的去中心化是指的账本去中心化,每个人都拥有完整的交易记录。因此不会出现人为修改某一个账本就导致财产丢失的情况。 在这种模式下,
俺踏月色而来
2018/06/20
1.2K0
TiFlash 源码解读(八)TiFlash 表达式的实现与设计
表达式是承载 SQL 大部分逻辑的一个重要部分。SQL 中的表达式和编程语言中的表达式并没有差异。表达式可以大致分为函数、常量、列引用。如 select a + 1 from table 中的 a + 1 是一个表达式,其中 + 是函数,1 是常量,a 是列引用。
PingCAP
2022/09/06
4740
基于Java语言构建区块链(六)—— 交易(Merkle Tree)
在这一系列文章的最开始部分,我们提到过区块链是一个分布式的数据库。那时候,我们决定跳过"分布式"这一环节,并且聚焦于"数据存储"这一环节。到目前为止,我们几乎实现了区块链的所有组成部分。在本篇文章中,我们将会涉及一些在前面的文章中所忽略的一些机制,并且在下一篇文章中我们将开始研究区块链的分布式特性。
王维
2018/04/16
1.5K4
基于Java语言构建区块链(六)—— 交易(Merkle Tree)
Android 动态分区概念了解
从Android Q引入动态分区,到Android R/S在动态分区之上增加虚拟分区管理, OTA升级时需要对分区变更进行处理
wizzie
2022/09/28
2.2K1
Android 动态分区概念了解
【杨镇】【中译修订版】以太坊的分片技术官方介绍
本文的目的是为那些希望理解分片建议详情,乃至去实现它的朋友提供一份相对完整的细节说明和介绍。本文仅作为二次方分片(quadratic sharding)的第一阶段的描述;第二、三、四阶段目前不在讨论范围,同样,超级二次方分片(super-quadratic sharding)(“Ethereum 3.0”) 也不在讨论范围。
圆方圆学院
2018/11/12
6370
【技术分享】深入了解tensorflow模型存储格式
做模型的同学基本都会使用tensorflow,不知道大家是否会像我一样对tensorflow的模型存储感到疑惑:各种模型保存的方法、保存出的模型文件名称和结构还不一样、加载模型的时候有的需要重新定义一遍计算图而有的不需要、有的格式tfserving能用有的不能用。这篇文章会带大家了解每个模型文件分别包含什么内容、计算图是以什么样的形式保存在文件中的。
腾讯云TI平台
2020/03/18
3.2K0
MySQL迁移OpenGauss原理详解
数据迁移是指将数据从一个数据库迁移至另一个数据库,按照数据库类型来分类,可分为同构数据库之间的迁移和异构数据库之间的迁移。
炒香菇的书呆子
2023/12/06
1.7K1
MerkleTree in BTC
Merkle 树是一种用于高效且安全地验证大数据结构完整性和一致性的哈希树。它在比特币网络中起到至关重要的作用。Merkle 树是一种二叉树结构,其中每个叶子节点包含数据块的哈希值,每个非叶子节点包含其子节点哈希值的组合哈希。
孟斯特
2024/07/04
1900
MerkleTree in BTC
推荐阅读
相关推荐
Android 系统架构及HAL层概述
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验