2024年8月29日 星期四

How to repair and clone disk with ddrescue

 ddrescue is a tool that can be used to repair and clone disks on a Linux system. This includes hard drives, partitions, DVD discs, flash drives, or really any storage device. It performs data recovery by copying data as blocks.


If ddrescue encounters errors from the data it’s trying to copy, it can discard them and keep only the good data. This makes it an ideal tool when trying to recover data from a corrupted disk. In this tutorial, you will learn how to install ddrescue and use it to clone a full disk or partition, and write that data to an empty storage space.

To install ddrescue on UbuntuDebian, and Linux Mint:

$ sudo apt install gddrescue

Clone a partition to image file or other disk

In the section, we will use ddrescue to clone a partition or full disk (the process is the same) to an image file. That file can that be written to another disk or partition afterwards. We will also show the process to clone a partition directly to another disk, bypassing the image file creation and instead creating a direct clone onto new hardware.

  1. First, open a command line terminal and identify the device path to the hard drive or partition that you would line to clone. For this, you can use a tool like lsblkfdisk, etc.
    $ lsblk
    
    Here we find the device path /dev/sdb1 which is the partition we want to clone
    Here we find the device path /dev/sdb1 which is the partition we want to clone
  2. Note that the -d option will force ddrescue to ignore the kernel’s cache and instead access the disk directly.

    ddrescue process of cloning the partition to an image file
    ddrescue process of cloning the partition to an image file
  3. Note that if you are trying to recover data from a corrupted disk, you may want to append the -r option after the first try above. This will instruct ddrescue to retry bad sectors in an effort to recover as much data as possible. You can specify the number of retries after the option. In this example, we will use 3 retries.
    $ sudo ddrescue -d -r3 /dev/sdX backup.img backup.logfile
    
  4. Next, we will copy the new image file to a different disk or partition. We can use an ordinary dd command for this.
    $ sudo dd if=backup.img of=/dev/sdX
    

    Alternatively, the ddrescue command can be used.

    $ sudo ddrescue -f backup.img /dev/sdX clone.logfile
    

    The -f option indicates that we are sending our output to a block device rather than a file.

  5. If you want to clone a disk or partition directly to another, thereby bypassing any image file, you can do so with the following syntax. In this example, we are cloning partition /dev/sdX1 to /dev/sdX2.
    $ sudo ddrescue -d -f /dev/sdX1 /dev/sdX2 clone.logfile
    
  6. Next, we will use the following command syntax to copy the partition to an image file. We are using /dev/sdX in the example below, but you would just need to substitute your own partition or device in place of it. The contents will be written to a file called backup.img.
  7. $ sudo ddrescue -d /dev/sdX backup.img backup.logfile
    


    After completing the steps above, you can access the cloned storage and will hopefully see all of your files there, assuming that ddrescue was successful in recovering them.

PFC优先级流量控制

From :https://support.huawei.com/enterprise/zh/doc/EDOC1100138438/d1e17776

PFC(Priority-based Flow Control,基于优先级的流量控制)也称为Per Priority Pause或 CBFC(Class Based Flow Control),是对Pause机制的一种增强。当前以太Pause机制(IEEE 802.3 Annex 31B)也能达到无丢包的要求,原理如下:当下游设备发现接收能力小于上游设备的发送能力时,会主动发Pause帧给上游设备,要求暂停流量的发送,等待一定时间后再继续发送数据。但是以太Pause机制的流量暂停是针对整个接口,即在出现拥塞时会将链路上所有的流量都暂停。

而PFC允许在一条以太网链路上创建8个虚拟通道,并为每条虚拟通道指定一个优先等级,允许单独暂停和重启其中任意一条虚拟通道,同时允许其它虚拟通道的流量无中断通过。这一方法使网络能够为单个虚拟链路创建无丢包类别的服务,使其能够与同一接口上的其它流量类型共存。

图2-1 PFC的工作机制

图2-1所示,DeviceA发送接口分成了8个优先级队列,DeviceB接收接口有8个接收缓存(buffer),两者一一对应(报文优先级和接口队列存在着一一对应的映射关系),形成了网络中 8 个虚拟化通道,缓存大小不同使得各队列有不同的数据缓存能力。

当DeviceB的接口上某个接收缓存产生拥塞时,即某个设备的队列缓存消耗较快,超过一定阈值(可设定为端口队列缓存的 1/2、3/4 等比例),DeviceB即向数据进入的方向(上游设备DeviceA)发送反压信号“STOP”。

DeviceA接收到反压信号,会根据反压信号指示停止发送对应优先级队列的报文,并将数据存储在本地接口缓存。如果DeviceA本地接口缓存消耗超过阈值,则继续向上游反压,如此一级级反压,直到网络终端设备,从而消除网络节点因拥塞造成的丢包。

“反压信号”实际上是一个以太帧,其具体报文格式如图2-2所示。
图2-2 PFC帧格式
表2-1 PFC帧的定义

项目

描述

Destination address

目的MAC地址,取值固定为01-80-c2-00-00-01。

Source address

源MAC地址。

Ethertype

以太网帧类型,取值为88-08。

Control opcode

控制码,取值为01-01。

Priority enable vector

反压使能向量。

其中E(n)和优先级队列n对应,表示优先级队列n是否需要反压。当E(n)=1时,表示优先级队列n需要反压,反压时间为Time(n);当E(n)=0时,则表示该优先级队列不需要反压。

Time(0)~Time(7)

反压定时器。

当Time(n)=0时表示取消反压。

Pad

预留。

传输时为0。

CRC

循环冗余校验。

总而言之,设备会为端口上的8个队列设置各自的PFC门限值,当队列已使用的缓存超过PFC门限值时,则向上游发送PFC反压通知报文,通知上游设备停止发包;当队列已使用的缓存降低到PFC门限值以下时,则向上游发送PFC反压停止报文,通知上游设备重新发包,从而最终实现报文的无丢包传输。

由此可见,PFC中流量暂停只针对某一个或几个优先级队列,不针对整个接口进行中断,每个队列都能单独进行暂停或重启,而不影响其他队列上的流量,真正实现多种流量共享链路。而对非PFC控制的优先级队列,系统则不进行反压处理,即在发生拥塞时将直接丢弃报文。

但是网络中如果出现大量PFC反压帧,则极有可能诱发网络死锁,出现两个或多个队列发生永久堵塞(等待),每个队列都在等待被其他队列占用并堵塞了的资源,最终导致网络系统性风险。智能无损网络提供了PFC死锁检测功能。当设备在死锁检测周期内持续收到反压帧时,将不会响应,确保不出现PFC死锁情况。

无损与有损的概念

由上述内容可知,报文在以太网络中的无丢包传输是通过PFC流控机制实现的。设备支持基于802.1p优先级的PFC和基于DSCP优先级的PFC:

  • 基于802.1p优先级的PFC:设备将报文中的802.1p优先级值与端口队列一一对应,即优先级值为0对应0号队列、优先级值为1对应1号队列,以此类推。
  • 基于DSCP优先级的PFC:设备根据配置的DiffServ域将报文中的DSCP优先级映射为内部优先级,内部优先级与端口队列一一对应,具体对应关系参见CloudEngine 12800, 12800E系列交换机 配置指南-QoS》中的“优先级映射配置”。

根据报文在网络中传输时是否需要无丢包传输,可以将业务划分为无损业务和有损业务。

  • 无损业务:需要无丢包传输的业务。使能了PFC功能的802.1p优先级或由DSCP优先级映射的内部优先级即为无损优先级,该优先级对应的队列即为无损队列。
  • 有损业务:允许丢包传输的业务。未使能PFC功能的802.1p优先级或由DSCP优先级映射的内部优先级即为有损优先级,该优先级对应的队列即为有损队列

2023年6月26日 星期一

How to use simple speedtest in RaspberryPi CLI

 

pi@ChunchaiRPI2:/tmp $  wget -O speedtest-cli https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.py

--2023-06-26 10:43:47--  https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.py

Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.111.133, 185.199.109.133, 185.199.110.133, ...

Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.111.133|:443... connected.

HTTP request sent, awaiting response... 200 OK

Length: 65334 (64K) [text/plain]

Saving to: ‘speedtest-cli’


speedtest-cli                                        100%[=====================================================================================================================>]  63.80K  --.-KB/s    in 0.08s


2023-06-26 10:43:48 (849 KB/s) - ‘speedtest-cli’ saved [65334/65334]


pi@ChunchaiRPI2:/tmp $ sudo chmod +x speedtest-cli

pi@ChunchaiRPI2:/tmp $ ./speedtest-cli --simple

Ping: 7.342 ms

Download: 301.20 Mbit/s

Upload: 251.35 Mbit/s

pi@ChunchaiRPI2:/tmp $


2023年6月20日 星期二

How to decompile dtb file (Device Tree)

dtc -I dtb -O dts -o devicetree.dts devicetree.dtb


$ sudo apt-get install device-tree-compiler


$ dtc -I dtb -O dts test.dtb > test.dts

$ dtc -I dts -O dtb test.dts > test.dtb


 


reference: https://forum.digilentinc.com/topic/2427-how-to-decompile-dtb-file/



Device Tree, reference:

Device Tree(一):背景介绍  http://www.wowotech.net/device_model/why-dt.html

Device Tree(二):基本概念  http://www.wowotech.net/device_model/dt_basic_concept.html

Device Tree(三):代码分析  http://www.wowotech.net/device_model/dt-code-analysis.html

Device Tree(四):文件结构解析 http://www.wowotech.net/device_model/dt-code-file-struct-parse.html

2023年6月1日 星期四

HGU – Supported Service Scenarios

From https://halny.com/knowledge-base/hgu-supported-service-scenarios/


HGU mode allows to flow multiple traffic classes across VEIP.
All UNI interfaces are belonged to one VEIP and it cannot be controlled by OMCI.
This non-OMCI part can be controlled by Web and Auto provisioning.
Most OLT vendors support dual stack:
– IP-HOST #1 -> MGMT (WEB, XML provisioning) – configure by OMCI (from OLT)
– VEIP (non-OMCI : INTERNET, VoIP, IPTV services) – configure by ONT WEB or provisioning



1.Bridge mode – only INTERNET:1-4/WIFI + MGMT

2.Bridge mode – INTERNET:1-4/WIFI, VoIP Interface + MGMT

3.Bridge mode – INTERNET:1-2/WIFI, IPTV:3-4, VoIP Interface + MGMT

4.Router mode – only INTERNET:1-4/WIFI + MGMT

5.Router mode – INTERNET:1-4/WIFI, VoIP Interface + MGMT

6.Router mode – INTERNET:1-2/WIFI, IPTV:3-4, VoIP + MGMT

SFU – Supported Service Scenarios

From: https://halny.com/knowledge-base/sfu-supported-service-scenarios/


1.Access mode – only Internet

2.Access mode – only IPTV

3.Transparent mode – Internet, IPTV, VoIP

4.VLAN translation – rBSA

5.802.1q in 802.1q Begin/End of Tunnel

6.Transparent 802.1q in 802.1q

7.OSE/MdO

OMCI協議二層功能的模型選擇

From:https://blog.csdn.net/JIANGXIN04211/article/details/48294645

我們知道有兩種大的二層功能,即MAC橋以及802.1p映射。
MAC橋是IEEE 802.1D描述的,有許多的特性,可以基於MAC地址透明轉發(True bridging)或VLAN characteristics(利用VLAN filter)。而映射功能描述了一個用戶側實體到1到8個網絡側流標記的關係。那種映射與只利用VLAN標記中pbit字段作為VLAN filters的MAC橋是相等的。


那兩種基本二層服務能組合實現各種連接要求。有三種大的基本模式:

N:1 bridging多個用戶端口在同一個橋中,而只有一個網絡服務

1:M mapping基於pbit來將一個用戶端口映射成多個網絡側服務

1:P filtering  基於非pbit的VLAN信息來將單一的用戶端口映射成多個網絡側服務

除了以上三個基本可能外,也有四種複雜的組合。即:

N:M bridging mapping顧名思義,N個用戶端口在一個橋中先橋轉發然後再進行基於pbit的映射。

1:MP map-filtering 一個用戶端口做filtering以及mapping

N:P bridging-filtering N個用戶端口在一個橋中先橋轉發然後再進行基於非pbit的VLAN信息的映射

N:MP bridging-map-filtering顯而易見。

系統性地,tag filtering發生在接近MAC橋而不是tagging操作的部位。如下順序:

ANI—Tag operation—Tag filtering—Bridging—Tag filtering—Tag operation—UNI

許多公司實現了非802.1p模型而採用最簡單的bridging模型,而最簡單一種是每個用戶端在一個Bridge中。1:P模型被採用。如果是多端口的CPE設備(一般由Interworking模塊,如Broadlight chip與交換Switch模塊,如Broadcom switch組成),那塊switch chip每個用戶口有個port VLAN(每個端口不一樣),為了區隔彼此。對於Untag流需要映射pbit的話可以採用管理對象(ID:171 Extended VLAN Tagging operation configuration data)屬性來達到要求。

 

參考:G.984.4 Section 8.2.2

2023年4月10日 星期一

secure boot(三)secure boot的签名和验签方案

From:https://cloud.tencent.com/developer/article/2182626?from=article.detail.2182624&areaSource=106000.1&traceId=6TuiWB62dtuJGznPqWlJ1

简介

FIT 格式支持存储镜像的hash值,并且在加载镜像时会校验hash值。这可以保护镜像免受破坏,但是,它并不能保护镜像不被替换。

而如果对hash值使用私钥签名,在加载镜像时使用公钥验签则可以保护镜像不被替换。因此,公钥必须保存在一个绝对安全的地方。

接下来的内容要求大家了解一些密码学的内容,之前也介绍过一些,可以看这篇文章

secure boot (一)FIT Image

secure boot (二)基本概念和框架

secure boot签名的大致流程:

  • 计算镜像的hash值
  • 利用私钥对hash值签名
  • 签名结果存在FIT Image 中。

secure boot验签的大致流程:

  • 读取FIT Image
  • 获得pubkey
  • 从FIT Image 提取签名
  • 计算镜像的hash
  • 使用公钥验签获得hash值,与计算得到的hash值进行对比

签名是由mkimage工具完成的,验签由uboot完成。





签名算法

原则上讲,任何合适的算法都可以用来签名和验签。在uboot中,目前只支持一类算法:SHA&RSA。

RSA 算法使用提前准备好的公钥就可以完成验签,验签相关的代码量也很少。在验签时,RSA只是在FDT中提取必要的数据进行校验。

当然也可以在uboot中添加合适的算法,如果有其他签名算法(如DSA),可以直接替换rsa.c,并在image-sig.c中添加对应算法即可。

创建RSA key和证书

openssl 创建一副2048的密钥对:

$ openssl genpkey -algorithm RSA -out keys/dev.key  -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537

创建包含pubkey的证书:

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

查看pubkey的值:

$ openssl rsa -in keys/dev.key -pubout

绑定设备树

在FIT Image的签名节点中需要添加以下 属性,签名节点与哈希节点处于同一级别,被称为signature@1, signature@2等。

  • algo: 算法名称
  • key-name-hint:用来签名的key。密钥对必须存放在单独的文件夹(mkimage 使用-k 参数指定),私钥被命名为 <name>.key,证书命名为<name>.crt

镜像被签名后,以下这些属性都会被自动强制添加:

  • value: 签名后的值(RSA-2048 占256 bytes)

以下这些属性是可选的:

  • timestamp:签名的时间
  • signer-name:签名者的名字(例如mkimage)
  • signer-version:签名的版本(例如"2013.01")
  • comment:签名者或者镜像的额外信息
  • sign-images:签名镜像的列表
  • hashed-nodes:签名者签名的节点列表,一般是包含节点完整路径的字符串。例如:
hashed-nodes = "/", "/configurations/conf@1", "/images/kernel@1",
  "/images/kernel@1/hash@1", "/images/fdt@1",
  "/images/fdt@1/hash@1";

以下是一个待签名镜像的its配置。

/dts-v1/;

/ {
 description = "Chrome OS kernel image with one or more FDT blobs";
 #address-cells = <1>;

 images {
  kernel@1 {
   data = /incbin/("test-kernel.bin");
   type = "kernel_noload";
   arch = "sandbox";
   os = "linux";
   compression = "none";
   load = <0x4>;
   entry = <0x8>;
   kernel-version = <1>;
   signature@1 {
    algo = "sha1,rsa2048";
    key-name-hint = "dev";
   };
  };
  fdt@1 {
   description = "snow";
   data = /incbin/("sandbox-kernel.dtb");
   type = "flat_dt";
   arch = "sandbox";
   compression = "none";
   fdt-version = <1>;
   signature@1 {
    algo = "sha1,rsa2048";
    key-name-hint = "dev";
   };
  };
 };
 configurations {
  default = "conf@1";
  conf@1 {
   kernel = "kernel@1";
   fdt = "fdt@1";
  };
 };
};

以下是配置项签名后的its文件。

/dts-v1/;

/ {
 description = "Chrome OS kernel image with one or more FDT blobs";
 #address-cells = <1>;

 images {
  kernel@1 {
   data = /incbin/("test-kernel.bin");
   type = "kernel_noload";
   arch = "sandbox";
   os = "linux";
   compression = "lzo";
   load = <0x4>;
   entry = <0x8>;
   kernel-version = <1>;
   hash@1 {
    algo = "sha1";
   };
  };
  fdt@1 {
   description = "snow";
   data = /incbin/("sandbox-kernel.dtb");
   type = "flat_dt";
   arch = "sandbox";
   compression = "none";
   fdt-version = <1>;
   hash@1 {
    algo = "sha1";
   };
  };
 };
 configurations {
  default = "conf@1";
  conf@1 {
   kernel = "kernel@1";
   fdt = "fdt@1";
   signature@1 {
    algo = "sha1,rsa2048";
    key-name-hint = "dev";
    sign-images = "fdt", "kernel";
   };
  };
 };
};

pubkey的存储

为了校验签名后的镜像,必须把pubkey存放在可信赖的位置。将pubkey存在镜像中是不安全的,很容易被破解。一般我们将其存放在uboot的FDT中(CONFIG_OF_CONTROL)。

pubkey应该作为一个子节点存放在/signature节点中。节点中要加上以下特性:

  • algo:算法名称
  • key-name-hint: 签名使用的key的名称
  • required: 校验某配置所使用的公钥

除此之外,每个算法都有一些必要的特性。RSA算法中,以下特性必须被添加:

  • rsa,num-bits:key的位数
  • rsa,modulus:N,多字节的整数
  • rsa,exponent:E,64位的无符号整数
  • rsa,r-squared:(2^num-bits)^2
  • rsa,n0-inverse:-1 / modulus[0] mod 2^32

下面看一个例子,以下是一个uboot.dtb存放RSA的例子。RSA key被mkimage打包在u-boot.dtb和u-boot-spl.dtb中,然后它们再被打包进u-boot.bin和u-boot-spl.bin。

ubuntu:~/uboot-nextdev$ fdtdump u-boot.dtb | less
/dts-v1/;
....
/ {
        #address-cells = <0x00000001>;
        #size-cells = <0x00000001>;
        compatible = "rockchip,rv1126-evb", "rockchip,rv1126";
        model = "Rockchip RV1126 Evaluation Board";
        // signature节点由mkimage工具自动插入生成,节点里保存了RSA-SHA算法类型、RSA核心因子参
   //数等信息。
        signature {
         key-dev {
    required = "conf";
          algo = "sha256,rsa2048";
          rsa,np = <0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x1327f633 0x00000003 0x00000003 0x00000003 0xc7aead6a 0xb4c79f40 0xa82bdf76 0xfb2f8387 0xa1e06dce 0xd451a706 0xc7f865e3 0x3e2d7ca8 0x6a71762e 0x125f1828 0x36ab1a41 0xb7e9e852 0x7bd0011a 0x7279e0b8 0xf37e189c 0x8cf00963 0x00000100 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000377 0x00000004 0x00000004 0x00000004 0x00000002 0x00000003 0x69616c40 0x00000003 0x6d634066 0x00000010 0x66633630 0x73797363>;
          rsa,c = <0x00000000>;
          rsa,r-squared = <0x00000000>;
          rsa,modulus = <0xc25ae693 0xc359f2a4 0xa866c89d 0xb7b1994f 0xf9f9f690 0x518d54a7 0xda0b83e8 0x06606e12 0x6ad1cbf9 0x92438edd 0x81e039c0 0x5d7322cc 0x124cdc80 0xa0c3288a 0x9265c3ae 0x6ac47a4b 0x00000003 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000008 0x00000003 0x00000003 0x00000003 0x00000002 0x73657300 0x2f736572 0x00000000 0x2f64776d 0x00000003 0x6d634066 0x00000001 0x30303000 0x726f636b 0x67726600 0x00000008 0x00000003 0x00000004 0x00000001 0x30303000 0x726f636b 0x706d7567 0x00000003 0x00001000 0x00000003 0x00000002 0x6e616765 0x30000000 0x726f636b 0x706d7500 0x00000008>;
          rsa,exponent-BN = <0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000003 0x00010001 0xe95771c5 0x00000800 0x64657600 0x616c6961 0x0000002c 0x30303030 0x00000034 0x30303000 0x2f64776d 0x00000002 0x65303030 0x0000001b 0x3132362d 0x00000003 0x00020000 0x00000003 0x00000002 0x65303230 0x0000001b 0x3132362d 0x6e000000 0xfe020000 0x00000042 0x0000006d 0x722d6d61 0x65303030 0x0000001b 0x3132362d 0x00000003 0x00001000 0x00000002 0x6e74726f 0x30000000 0x726f636b 0x706d7563 0x0000003e 0x00000004 0x00000004 0x00000004 0x00000000 0x00000050 0x636c6f63 0x40666634 0x00000014 0x2c727631 0x00000008>;
          rsa,exponent = <0x00000000 0x00000368>;
          rsa,n0-inverse = <0xe95771c5>;
          rsa,num-bits = <0x00000800>;
          key-name-hint = "dev";
};
};

签名方案

上一节内容提到过,在secure boot中一般使用RSA签名方案。

要完成对镜像的签名,就必须使用私钥。而私钥一般是存在服务器上的,在本地PC上只存公钥。要想完成对镜像的签名,就必须把所有镜像上传到服务器重新打包。这种方案上传的文件太多,比较繁琐。下面我们介绍一种常用的签名方案。



在PC上,存放一把公钥和临时私钥,公钥是打包进dtb中的,安全启动时使用。临时私钥是为了生成签名数据。

在本地打包时,使用临时私钥对非安全镜像签名,将签名数据上传到服务器使用真正的私钥进行二次签名。将二次签名的数据和非安全镜像打包在一起,就得到了安全镜像。安全启动时,从dtb中拿出公钥对安全镜像进行校验即可。

这样既可以保证私钥的安全,又避免了上传所有镜像签名的繁琐。

签名镜像+签名配置

在secure boot中,除了对各个独立镜像签名外,还要对FIT Image中的配置项进行签名。

有些情况下,已经签名的镜像也有可能遭到破坏。例如,也可以使用相同的签名镜像创建一个FIT image,但是,其配置已经被改变,从而可以选择不同的镜像去加载(混合式匹配攻击)。也有可能拿旧版本的FIT Image去替换新的FIT image(回滚式攻击)。

下面举个例子。

/ {
 images {
  kernel@1 {
   data = <data for kernel1>
   signature@1 {
    algo = "sha1,rsa2048";
    # kernel image镜像的哈希值,由mkiamge工具自动生成
    value = <...kernel signature 1...>
   };
  };
  kernel@2 {
   data = <data for kernel2>
   signature@1 {
    algo = "sha1,rsa2048";
    value = <...kernel signature 2...>
   };
  };
  fdt@1 {
   data = <data for fdt1>;
   signature@1 {
    algo = "sha1,rsa2048";
    vaue = <...fdt signature 1...>
   };
  };
  fdt@2 {
   data = <data for fdt2>;
   signature@1 {
    algo = "sha1,rsa2048";
    vaue = <...fdt signature 2...>
   };
  };
 };
 configurations {
  default = "conf@1";
  conf@1 {
   kernel = "kernel@1";
   fdt = "fdt@1";
  };
  conf@1 {
   kernel = "kernel@2";
   fdt = "fdt@2";
  };
 };
};

两个kernel image 都已经被签名了,但是,攻击者可以很容易的将kernel1 和fdt2 作为configuration 3去加载。

 configurations {
  default = "conf@1";
  conf@1 {
   kernel = "kernel@1";
   fdt = "fdt@1";
  };
  conf@1 {
   kernel = "kernel@2";
   fdt = "fdt@2";
  };
  conf@3 {
   kernel = "kernel@1";
   fdt = "fdt@2";
  };
 };

攻击者可以拿到签名的镜像,并且镜像是正确的。这种组合式攻击会给设备带来很大风险。

因此,为了解决这个问题,除了给镜像签名外,我们可以把配置选项也签名,每个镜像都有自己的签名,在给配置选项签名时,把镜像的hash值也包含进去。具体例子如下:

/ {
 images {
  kernel@1 {
   data = <data for kernel1>
   hash@1 {
    algo = "sha1";
    value = <...kernel hash 1...>
   };
  };
  kernel@2 {
   data = <data for kernel2>
   hash@1 {
    algo = "sha1";
    value = <...kernel hash 2...>
   };
  };
  fdt@1 {
   data = <data for fdt1>;
   hash@1 {
    algo = "sha1";
    value = <...fdt hash 1...>
   };
  };
  fdt@2 {
   data = <data for fdt2>;
   hash@1 {
    algo = "sha1";
    value = <...fdt hash 2...>
   };
  };
 };
 configurations {
  default = "conf@1";
  conf@1 {
   kernel = "kernel@1";
   fdt = "fdt@1";
   signature@1 {
    algo = "sha1,rsa2048";
    # 对配置项签名,由mkimage工具自动生成
    value = <...conf 1 signature...>;
   };
  };
  conf@2 {
   kernel = "kernel@2";
   fdt = "fdt@2";
   signature@1 {
    algo = "sha1,rsa2048";
    value = <...conf 1 signature...>;
   };
  };
 };
};

如上所示,除了给所有镜像添加了hash值,还为每个配置添加了签名。mkimage将会对configurations/conf@1签名(/images/kernel@1, /images/kernel@1/hash@1,/images/fdt@1, /images/fdt@1/hash@1) 。签名会被写入 /configurations/conf@1/signature@1/value

验签

FIT image 在加载时会验签。如果'required' 指定了验签的公钥,则会使用这把公钥校验该配置对应的所有镜像。

为了支持FIT格式,以下配置项必须被选上。

CONFIG_FIT_SIGNATURE :使能FIT image的签名和验签

CONFIG_RSA :使能RSA签名算法

默认情况下,使能FIT Image的签名和验签后,CONFIG_IMAGE_FORMAT_LEGACY会被禁用。即FIT uboot image的只能引导FIT kernel Image。 如果需要引导legacy kernel image,需要手动添加CONFIG_IMAGE_FORMAT_LEGACY 定义。

测试

为了校验签名和验签是否正确,可以使用测试脚本test/vboot/vboot_test.sh。下面以sandbox为例子来说明bootm的启动和对镜像的验签。

$ make O=sandbox sandbox_config
$ make O=sandbox
$ O=sandbox ./test/vboot/vboot_test.sh
/home/hs/ids/u-boot/sandbox/tools/mkimage -D -I dts -O dtb -p 2000
Build keys
do sha1 test
Build FIT with signed images
Test Verified Boot Run: unsigned signatures:: OK
Sign images
Test Verified Boot Run: signed images: OK
Build FIT with signed configuration
Test Verified Boot Run: unsigned config: OK
Sign images
Test Verified Boot Run: signed config: OK
check signed config on the host
Signature check OK
OK
Test Verified Boot Run: signed config: OK
Test Verified Boot Run: signed config with bad hash: OK
do sha256 test
Build FIT with signed images
Test Verified Boot Run: unsigned signatures:: OK
Sign images
Test Verified Boot Run: signed images: OK
Build FIT with signed configuration
Test Verified Boot Run: unsigned config: OK
Sign images
Test Verified Boot Run: signed config: OK
check signed config on the host
Signature check OK
OK
Test Verified Boot Run: signed config: OK
Test Verified Boot Run: signed config with bad hash: OK

Test passed

完整校验流程

OTP校验loader

那么,这种镜像校验方式有个很重要的问题,公钥存在哪里才是安全的呢?

一般SOC中会有一个叫OTP或EFUSE的区域,这部分区域比较特殊,只可以写入一次,写入后就再也不可以修改了。把公钥存储在OTP中,就可以很好地保证其不能被修改。

OTP的存储空间很小,一般只有几KB,因此并不适合直接存放RSA公钥。一般都是将RSA公钥的hash val 存放在OTP中。像sha256的hash值仅为256 bits,而RSA 公钥本身一般存放在镜像中。

在使用公钥之前,只需要使用OTP中的公钥hash值验证镜像附带公钥的完整性,即可确定公钥是否合法。



RSA公钥需要一般使用芯片厂家的工具写入loader。安全启动时,bootrom首先从loader固件头中获取RSA公钥并校验合法性;然后再使用该公钥校验SPL的固件签名。

spl校验uboot

SPL把RSA公钥保存在u-boot-spl.dtb中,u-boot-spl.dtb会被打包进u-boot-spl.bin文件(最后打包进loader);安全启动时SPL从自己的dtb文件中拿出RSA公钥对uboot.img进行安全校验。

uboot校验kernel

U-Boot把RSA公钥保存在u-boot.dtb中,u-boot.dtb会被打包进u-boot.bin文件(最后打包为uboot.img);安全启动时U-Boot从自己的dtb文件中拿RSA公钥对boot.img进行校验。

总结

从bootrom到kernel为止的安全启动,统一使用一把RSA公钥完成安全校验,并且当前这级的RSA Key已经作为自身固件的一部分,由前一级loader完成了安全校验,从而保证了Key的安全。

How to repair and clone disk with ddrescue

  ddrescue  is a tool that can be used to repair and clone disks on a  Linux system . This includes hard drives, partitions, DVD discs, flas...