从SDK 12开始,Nordic为DFU操作增加了签名校验机制,称为Secure DFU。执行Secure DFU,需要使用升级包(zip),而不能直接使用二进制文件(hex/bin),本文介绍如何生成和使用升级包。

(1)nrfutil

nrfutil是Nordic提供的命令行工具,它用于执行以下任务:

  • 生成公钥、私钥
  • 生成升级包
  • 生成bootloader_settings.hex
  • 执行BLE DFU或串口DFU

nrfutil是开源的python项目,托管在github上,可以下载预编译的nrfutil.exe,如果系统中已安装了python 2.7(不支持python 3.x),也可以通过pip命令进行安装:

pip install nrfutil

本文使用预编译的nrfutil.exe,放到C:\nordic\nrfutil中,并将该目录加入系统环境变量,方便在cmd中调用。

(2)公钥、私钥

Secure DFU采用签名(Sign)方式保证DFU的安全。直观上,它解决了一个安全问题:

对于传统的DFU,如果攻击者进入设备的Bootloader模式,那么他可以通过DFU将第三方的固件写入设备。这种非法升级的问题根源在于Bootloader无法识别新固件是否合法有效。

Secure DFU在Bootloader和升级包中分别加入签名,升级时通过校验签名保证升级包的安全和可靠。

在当前目录下生成一个唯一的私钥(private_key.pem):

nrfutil keys generate private_key.pem

可以用文本编辑器打开private_key.pem,其内容形式如下:

-----BEGIN EC PRIVATE KEY-----
MHcCAQEEICS144KRk5kbsB42yuQHFutQJV3nVfh7vUtj80nokAsboAoGCCqGSM49
AwEHoUQDQgAEKlnoPDH7wA00TtqUjc+5tuoI8gov4BxZR9KUITgKAmx/v4Jvpx2t
jRI6meLVMSme4itY78N+YtuK7s0wLbNzUw==
-----END EC PRIVATE KEY-----

利用私钥,可以生成一个公钥:

nrfutil keys display 
    --key pk 
    --format code private_key.pem 
    --out_file dfu_public_key.c

执行完毕,生成的公钥保存在dfu_public_key.c文件中,其文件内容形式如下:

/* This file was automatically generated by nrfutil on 2018-04-14 (YY-MM-DD) at 08:29:52 */

#include "stdint.h"
#include "compiler_abstraction.h"

/** @brief Public key used to verify DFU images */
__ALIGN(4) const uint8_t pk[64] =
{
    0x6c, 0x02, 0x0a, 0x38, 0x21, 0x94, 0xd2, 0x47, 0x59, 0x1c, 0xe0, 0x2f, 0x0a, 0xf2, 0x08, 0xea, 0xb6, 0xb9, 0xcf, 0x8d, 0x94, 0xda, 0x4e, 0x34, 0x0d, 0xc0, 0xfb, 0x31, 0x3c, 0xe8, 0x59, 0x2a, 
    0x53, 0x73, 0xb3, 0x2d, 0x30, 0xcd, 0xee, 0x8a, 0xdb, 0x62, 0x7e, 0xc3, 0xef, 0x58, 0x2b, 0xe2, 0x9e, 0x29, 0x31, 0xd5, 0xe2, 0x99, 0x3a, 0x12, 0x8d, 0xad, 0x1d, 0xa7, 0x6f, 0x82, 0xbf, 0x7f
};

Bootloader工程中会使用公钥dfu_public_key.c,这样Bootloader就可以利用公钥验证签名。

(3)升级包

升级包其实就是一个zip压缩包,一个典型的升级包内容如下:

各个文件的含义:

文件 含义
manifest.json 文件清单
nrf52832_xxaa.bin 新固件
nrf52832_xxaa.dat init packet

其中init packet包含了meta信息:新固件的类型、大小、版本和签名信息。这里的签名将在执行DFU被校验。

init packet是一个Protocol Buffer文件,它的内容很简单,但是不可读,可以通过命令nrfutil pkg display dfu_pkg.zip看到它的内容:

我们无需手动创建init packet,使用nrfutil生成升级包时,会自动完成这些工作。

生成升级包至少需要以下文件和信息:

  • 新固件文件
  • 私钥文件
  • 芯片型号
  • 支持的softdevice版本号
  • 应用程序版本号

假设新固件为app.hex,私钥文件为private_key.pem,芯片型号为nRF52,softdevice版本号为S132_nrf52_5.1.0,新固件版本号为0x02,最终生成的升级包文件为dfu_pkg.zip,那么对应的命令为:

nrfutil pkg generate 
    --application app.hex 
    --hw-version 52 
    --sd-req 0x5A 
    --application-version 0x02 
    --key-file private_key.pem 
    dfu_pkg.zip

命令中各参数含义很直观,值得一提的是--sd-req这个参数,它表示固件运行所需的softdevice版本,0x5A表示softdevice S132_nrf52_5.1.0。

获得sd-req的数值至少有以下几种途径:

  1. 通过命令nrfutil pkg generate --help查看
  2. 在线文档论坛帖子中查看
  3. 烧录一个BLE程序,在nrfgo Studio中查看
  4. 使用JFlash.exe打开softdevice.hex,找到地址0x300C的值(原理
  5. 利用Hex2Bin转换工具(开源版Keil版)将softdevice.hex转换成bin文件,然后利用任意十六进制查看器,查看地址0x300C的值
  6. 询问本文作者

如果在命令行中加入--debug-mode参数,就可以在DFU的校验阶段,忽略sd-req信息。

(4)执行DFU

现在有了升级包,可以利用nrfutil执行DFU,从而省去将升级包复制到手机,再利用APP做DFU的复杂流程。

假设DFU的目标设备是一个nRF52832开发板,先对其下载softdevice和bootloader,这时能够看到Bootloader正在广播,我这里BLE设备名称为DfuTest。

准备一个nRF51开发板(PCA10028)作为DFU的操作主机,芯片中不用预先下载任何程序,连上电脑后记录它的串口号COMx。

执行命令:

nrfutil dfu ble 
    -pkg dfu_pkg.zip 
    -ic NRF51 
    -p COM6 
    -n DfuTest 
    -f

最后的参数-f将自动给主机设备下载必要的固件程序。

这样就可以利用COM6对应的nRF51设备,向DfuTest设备做DFU升级,升级文件为dfu_pkg.zip,升级过程能看到一个进度条:

 

(完)

DFU(Device Firmware Update)指对设备进行固件更新,有的地方也叫OTA(Over The Air)。Nordic提供了非常完善的DFU方案,而且操作也很简单。

本文演示执行BLE DFU的过程。

(1)准备工作

平台:Windows,安卓手机

硬件:nRF52开发板(PCA10040)

软件:nrfjprog

SDK:SDK 15.0.0

APP:nRF ConnectTotal Commander

(2)应用场景

实际中可能有三种应用场景:

从Bootloader升级:先对裸片烧录Softdevice和Bootloader,然后在Bootloader中执行DFU升级。

从Application升级:芯片中已有一个应用程序,先跳转进入Bootloader,再执行DFU升级。利用Buttonless服务,无需触发按键,仅通过BLE控制实现自动跳转进入Bootloader。

烧录Application:这不算是一种DFU升级,通常会在工厂量产阶段,采用这种方式。

(3)从Bootloader升级

进入文件夹<sdk>\examples\dfu\secure_dfu_test_images\ble\nrf52832

操作中将要使用到以下文件:

  • softdevice_s132.hex
  • bootloader_secure_ble_debug_without_bonds_s132.hex
  • hrs_application_s132.zip

依次烧录前两个文件,使用下面命令行:

@echo off

nrfjprog -e
nrfjprog --program ./softdevice_s132.hex
nrfjprog --program ./bootloader_secure_ble_debug_without_bonds_s132.hex
nrfjprog --reset

pause

打开nrf connect,应该可以看到一个dfuTest的广播设备:

将hrs_application_s132.zip复制到安卓手机中,记下它的路径,稍后会使用。

连接dfuTest设备,点击右上角的DFU图标,将打开如下界面:

选择第一项:Distribution Packet(ZIP)。

假设手机上已经安装好了Total Commander软件,则能看到下图界面,选择Total Commander:

找到并选择hrs_application_s132.zip,即开始进行DFU:

DFU结束后,可以看到Nordic_HRM设备。

至此就完成了整个DFU工作,本文后面的内容都是基于这个流程做的延伸。

(4)从Application升级

如果用户设备中已经烧录了Application,我们需要从Application跳转进入Bootloader,然后再进行DFU,升级成新的Application。

操作中将要使用到以下文件:

  • sd_s132_bootloader_buttonless_with_setting_page_dfu_secure_ble_debug_without_bonds.hex
  • hrs_application_s132.zip

第一个hex文件包含了softdevice、bootloader和一个application。将它烧录到芯片中:

@echo off

nrfjprog -e
nrfjprog --program ./sd_s132_bootloader_buttonless_with_setting_page_dfu_secure_ble_debug_without_bonds.hex
nrfjprog --reset

pause

打开nrf connect,找到设备Nordic_Buttonless:

连接它,点击右上角的DFU图标,并重复上一节相同的步骤:

  • 选择Distribution Packet(ZIP)
  • 选择刚才从PC复制的hrs_application_s132.zip
  • 执行DFU

考虑整个流程, 我们并没有手动断开Application,然后跳转到Bootloader,这一切都由nrf connect自动完成。

(5)烧录Application

在量产阶段,我们需要将Application直接烧录到设备中。如果尝试依次烧录softdevice、bootloader和application,会发现设备一直跑在bootloader中,而没有进入application。

原因是bootloader会检测application的有效性,包括固件大小和CRC等信息。这些信息称为Bootloader Settings,保存在flash的bootloader_settings段中,如下:

Bootloader只有检测到有效信息,才会进入Application中。DFU过程中会自动生成和写入这些信息,而直接烧录则不会,于是Bootloader认为Flash中没有一个有效的Application,进而不做任何跳转动作。

通过application.hex可以生成bootloader_settings.hex,该hex中包含了全部的Bootloader Settings信息。烧录该hex,Bootloader就可以检测到有效性信息,完成跳转。

找到ble_app_buttonless_dfu_without_bonds_s132_with_setting_page.hex文件,该hex即application.hex与bootloader_settings.hex合并后的文件。

依次烧录softdevice、bootloader以及application_with_setting,使用如下命令:

@echo off

nrfjprog -e
nrfjprog --program ./softdevice_s132.hex
nrfjprog --program ./bootloader_secure_ble_debug_without_bonds_s132.hex
nrfjprog --program ./ble_app_buttonless_dfu_without_bonds_s132_with_setting_page.hex

pause

在nrf connect中,看到Nordic_Buttonless设备,即表示烧录成功。

 

(完)