Nordic52832 Secure DFU

1 篇文章 0 订阅
1 篇文章 0 订阅

#Nordic52832 Secure DFU

文章目录


从nrf52832的SDK 12.0以后,提供的DFU的例子为bootloader_secure。

bootloader_secure 相对于以前版本的 dual_bank ,single bank 有如下优点:

  • -对升级文件进行了ECSDA签名,防止误升级了未授权的程序
  • -对升级的firmware版本进行了管理,防止升级回到低版本。
    ##ECDSA算法的实现
    ###签名算法DSA和ECDSA

比特币协议里使用了ECDSA(椭圆曲线签名算法),我之前以为它和基于大数分解的RSA公钥密码体系差不多。这两天看了下维基百科,才发现它们之间的差异挺大。最本质的区别是,DSA和ECDSA都只是签名算法,它用来确保信息发布人的身份和信息的完整性,不能用来做加密传输,为了实现这个功能,信息的原文(或者HASH摘要)必须随着签名一起传输和公布才能被验证。而RSA是公钥加密体系,它可以用来加密传输(即信息原文在传输中加密,到达对方后解密),它也可以实现签名验证。

DSA和ECSDA的基本架构和RSA一样,签名者持有私钥,对应公钥向全世界公开。当需要对信息签名时,签名者用私钥对信息签名,然后将签名信息和信息原文发给对方(RSA协议中,信息原文不需要发给对方,签名信息解密后就是信息原文),验证者可用签名者公开的公钥对签名信息和信息原文验证签名。由于信息长度可能比较长,在实际操作中,大家通常在信息的HASH摘要上进行签名。

具体内容可以参考网页

  • ECDSA (Elliptic Curve Digital Signature Algorithm), an algorithm to create and verify signatures for any kind of data (for example, a firmware image)
  • ECDH (Elliptic Curve Diffie-Hellmann), a key agreement protocol that allows to establish a shared secret over an insecure channel
    ###NRF52832 实现ECDSA
    NRF52832 SDK采用了micro-ecc开源软件实现ECDSA算法

Installing micro-ecc
micro-ecc is an open source library that is required to use the cryptography library. When using micro-ecc, you must ensure compliance with the license of the library as stated in the LICENSE.txt file that is included in micro-ecc.

The cryptography library expects to find the compiled micro-ecc library in InstallFolder\external\micro-ecc\micro-ecc.

To install micro-ecc, complete the following steps:

Install version 4.9-2015-q3-update of the GCC compiler toolchain for ARM. You can use ARM’s Launchpad to find the toolchain for your operating system.

Make sure that make is installed (see, for example, MinGW, GNU Make, or Xcode).

Clone the micro-ecc GitHub repository into InstallFolder\external\micro-ecc\micro-ecc.

Enter the subdirectory for the SoC and the toolchain that you are using to build your application:
InstallFolder\external\micro-ecc\nrf51_keil\armgcc

InstallFolder\external\micro-ecc\nrf52_keil\armgcc

InstallFolder\external\micro-ecc\nrf51_iar\armgcc

InstallFolder\external\micro-ecc\nrf52_iar\armgcc

InstallFolder\external\micro-ecc\nrf51_armgcc\armgcc

InstallFolder\external\micro-ecc\nrf52_armgcc\armgcc

Run make to compile the micro-ecc library.

Note:

If you compile the micro-ecc library without using the provided Makefiles, make sure to use the default compilation options with -Os and optimization level 3. If you change the preprocessor macros (most importantly,uECC_VLI_NATIVE_LITTLE_ENDIAN=1), the library might not work prop

由于micro-ecc使用了汇编代码实现了fast-multfast_square 等数学运算,而micro-ecc的汇编是用arm-gcc编译的,所以必须下载arm-gcc工具。arm-gcc需要用到Makefile,这样在windows平台上需要安装gnu工具。

下载gcc-arm 和gnuarmelipse插件,设置相应的path环境变量开始编译,遇到两个问题:

  1. 在D:\ble\nrf52832\nRF5_SDK_12.0.0\components\toolchain\gcc目录中需要修改Makefile.common的gcc-arm路径
  2. 需要手动在编译目录创建_build 文件夹。编译生成micro_ecc_lib_nrf52.lib

将编译生成的micro_ecc_lib_nrf52.lib文件加到dfu_secure工程中,编译通过,但是仿真是不能看到源代码,提示加载.axf出错。根据KEIL网站上查询得知,是对于源代码不是用keil的编译器编译的工程,不能通过源代码在MDK内进行仿真。
###PC端工具
Nordic PC端工具 nrfutil的github地址为:
nrfutil github

当前下载到的版本是v2.1.0,版本日志:
There are 2 different and incompatible DFU package formats:

  • legacy: used a simple structure and no security
  • modern: uses Google’s protocol buffers for serialization and can be cryptographically signed

The DFU package format transitioned from legacy to modern in SDK 12.0. Depending on the SDK version that you are using you will need to select a release of this tool compatible with it:

  • Version 0.5.2 generates legacy firmware packages compatible with nRF SDK 11.0 and older
  • Versions 1.5.0 and later generate modern firmware packages compatible with nRF SDK 12.0 and newer

nrfutil 是用python2.7写的,所以PC上要先安装Python2.7。如果PC上已经安装了Python3.5,可以通过修改PC的环境变量来切换3.5和2.7环境。nrfutil的包的依赖为:

six >= 1.9
pyserial >= 2.7  # 用于控制pc端串口进行升级
enum34 >= 1.0.4
click >= 6.0
ecdsa >= 0.13 # ECDSA 签名算法 
behave
protobuf   # protocol buffers
pc_ble_driver_py >= 0.8.0

可以通过 pyinstaller nrfutil.spec 生成nrfutil.exe 工具。
nrfutil 的用法:

生成keys

D:\ble\nrf52832\pc-nrfutil-master\dist>nrfutil keys generate priv.pem
Generated private key and stored it in: priv.pem

keys文件内容,通过base64编码

-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIMkpi9zq/YM18l5/QyC7W6esgh3k9Zwm9hS/wXrlrDwZoAoGCCqGSM49
AwEHoUQDQgAEFy45JuGB+tE9OiBW6SWol16M0m/M/G+vA+FrqPcHtQDbovSTV8cG
osnlpOCzHKmLQ6vYmPtO7dKNQhwzE0f8KQ==
-----END EC PRIVATE KEY-----

keys查看,公钥

D:\ble\nrf52832\pc-nrfutil-master\dist>nrfutil keys display --key pk --format he
x priv.pem
Public (verification) key pk:
e4e248152b8911627592752f44b8ccd70082981d6feb0077cb24f14c631594f3a497f8581ba69c72
4cd9173187402a2d08f62d1df67dab774c5bf44ef671d581

keys查看,私钥

D:\ble\nrf52832\pc-nrfutil-master\dist>nrfutil keys display --key sk --format he
x priv.pem

WARNING: Security risk! Do not share the private key.

Private (signing) key sk:
6bf5ba64df2a2032ebd71edd7d3d4e9e067ecbabf2b0e39e79035f474d99c2bd

生成公钥.c文件

	D:\ble\nrf52832\pc-nrfutil-master\dist>nrfutil keys display --key pk --format co
	de  priv.pem

	/* This file was automatically generated by nrfutil on 2016-12-29 (YY-MM-DD) at
	13:08:48 */

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

	/** @brief Public key used to verify DFU images */
	__ALIGN(4) const uint8_t pk[64] =
	{
    0xe4, 0xe2, 0x48, 0x15, 0x2b, 0x89, 0x11, 0x62, 0x75, 0x92, 0x75, 0x2f, 0x44
	, 0xb8, 0xcc, 0xd7, 0x00, 0x82, 0x98, 0x1d, 0x6f, 0xeb, 0x00, 0x77, 0xcb, 0x24,
	0xf1, 0x4c, 0x63, 0x15, 0x94, 0xf3,
    0xa4, 0x97, 0xf8, 0x58, 0x1b, 0xa6, 0x9c, 0x72, 0x4c, 0xd9, 0x17, 0x31, 0x87
	, 0x40, 0x2a, 0x2d, 0x08, 0xf6, 0x2d, 0x1d, 0xf6, 0x7d, 0xab, 0x77, 0x4c, 0x5b,
	0xf4, 0x4e, 0xf6, 0x71, 0xd5, 0x81
	};

将生成的pk[64]数组替换掉NRF52832工程中dfu_public_key.c中的对应数组

文件打包

D:\ble\nrf52832\pc-nrfutil-master\dist>nrfutil pkg generate --hw-version 0x52 --
sd-req 0x8c --application-version 4 --application app.hex --key-file priv.pem df
u.zip
Zip created at dfu.zip

包文件查看

D:\ble\nrf52832\pc-nrfutil-master\dist>nrfutil pkg display dfu.zip

DFU Package: <dfu.zip>:
|
|- Image count: 1
|
|- Image #0:
	|- Type: application
	|- Image file: app.bin
	|- Init packet file: app.dat
		|
		|- op_code: INIT
		|- signature_type: ECDSA_P256_SHA256
		|- signature: 08b928a1656590802807405c37522eb2c11b67ff6b5688517d052cefcb8c
8fbbd10eaf3f5f7b6afab1c7c2bf21807f93c91dd1c9406ba036e62e9923c3c03d93
	  	|
	 	|- fw_version: 0x00000004 (4)
	  	|- hw_version 0x00000052 (82)
	  	|- sd_req: 0x8C
	   	|- type: APPLICATION
	  	|- sd_size: 0
	  	|- bl_size: 0
	  	|- app_size: 49844
	  	|
	  	|- hash_type: SHA256
	  	|- hash: d7fe4842195f7b9127d4b522ee5d6b4ef3e755677508093c7f0ed29249f0c154
	  	|
	    |- is_debug: False

zip包文件分析
nrfutil生成的*.zip文件是一个普通的zip文件,可以通过winrar等解压缩工具打开,解压以后可以发现.zip*包含了三个文件。

2016/12/29  13:12            49,844 app.bin
2016/12/29  13:12               135 app.dat
2016/12/29  13:12               138 manifest.json

其中app.bin文件为app.hex生成的bin文件,是需要发送给nrf52832的升级固件。
app.dta是根据 dfu-cc.proto生成的包含init_command的protocal buffers packet(可参考下一节内容)。
manifest.json的内容如下,描述*.zip*文件的内容:

    "manifest": {
        "application": {
            "bin_file": "app.bin",
            "dat_file": "app.dat"
        }
    }
}

PC nrfutil 代码分析
待补充
##protocol Buffers的实现

###protocol Buffers简介
google protocol buffers 的使用和原理
###nano-pb介绍
nano-pb document
nano-pb包含了protocol buffers 的decode,encode的c代码。也包含了一个用 python写generateor工具。该工具可以和google提供的protoc.exe 配合,将*.proto文件转换为对应的.c.h文件。

Linux 环境下测试 nano-pb的example
在linux环境下测试nano-pb附带的example中的network-server的例子。该例子实现在网络上通过protocol buffer打包的数据来传输目录的文件列表。用generator工具和protc通过fileproto.proto生成fileproto.pb.hfileproto.pb.c

protoc -ofileproto.pb fileproto.proto
python ../../generator/nanopb_generator.py fileproto.pb
    Writing to fileproto.pb.h and fileproto.pb.c

编译example代码,生成severclient

~/nano-pb/nanopb-master/examples/network_server ubuntu@WEB
❯ make
cc -ansi -Wall -Werror -g -O0 -I/home/ubuntu/nano-pb/nanopb-master -o server server.c common.c fileproto.pb.c /home/ubuntu/nano-pb/nanopb-master/pb_encode.c /home/ubuntu/nano-pb/nanopb-master/pb_decode.c /home/ubuntu/nano-pb/nanopb-master/pb_common.c
cc -ansi -Wall -Werror -g -O0 -I/home/ubuntu/nano-pb/nanopb-master -o client client.c common.c fileproto.pb.c /home/ubuntu/nano-pb/nanopb-master/pb_encode.c /home/ubuntu/nano-pb/nanopb-master/pb_decode.c /home/ubuntu/nano-pb/nanopb-master/pb_common.c

~/nano-pb/nanopb-master/examples/network_server ubuntu@WEB
❯ 

运行severclient进行测试

~/nano-pb/nanopb-master/examples/network_server ubuntu@WEB
❯ ./server &
[2] 1681
bind: Address already in use
[2]  + 1681 exit 1     ./server

~/nano-pb/nanopb-master/examples/network_server ubuntu@WEB
❯ ./client .                  
Got connection.
Listing directory: .
Closing connection.
281871     Makefile
298710     server
281876     common.c
298707     fileproto.pb.c
281877     common.h
281873     client.c
281883     server.c
281872     README.txt
281878     fileproto.options
281868     .
281881     fileproto.proto
281858     ..
298708     fileproto.pb.h
298733     client

###NRF52832的nano-pb的实现
NRF52832 dfu用的.proto文件 dfu-cc.proto

package dfu;

// Version 0.1

// Definition of enums and types
enum OpCode {
	RESET	= 0;
	INIT	= 1;
}

enum FwType {
	APPLICATION				= 0; // default, compatible with proto3
	SOFTDEVICE				= 1;
	BOOTLOADER				= 2;
	SOFTDEVICE_BOOTLOADER	= 3;
}

enum HashType {
	NO_HASH	= 0;
	CRC		= 1;
	SHA128	= 2;
	SHA256	= 3;
	SHA512	= 4;
}

message Hash
{
	required HashType 	hash_type	= 1;
	required bytes 		hash		= 2;
}

// Commands data
message InitCommand {
	optional uint32	fw_version	= 1;
	optional uint32	hw_version	= 2;
	repeated uint32	sd_req		= 3 [packed = true]; // packed option is default in proto3
	optional FwType	type		= 4;

	optional uint32	sd_size		= 5;
	optional uint32	bl_size		= 6;
	optional uint32	app_size	= 7;

	optional Hash	hash		= 8;
    
    optional bool   is_debug    = 9 [default = false];
}

message ResetCommand
{
	required uint32 timeout	= 1;
}

// Command type
message Command {
	optional OpCode			op_code	= 1;
	optional InitCommand	init 	= 2;
	optional ResetCommand	reset 	= 3;
}

// Signed command types
enum SignatureType {
	ECDSA_P256_SHA256	= 0;
	ED25519				= 1;
}

message SignedCommand {
	required Command		command			= 1;
	required SignatureType	signature_type	= 2;
	required bytes			signature		= 3;
}

// Parent packet type
message Packet {
	optional Command 		command			= 1;
	optional SignedCommand	signed_command	= 2;
}

分析该dfu-cc.proto文件,Packet中可包含普通的commandsigned_commandsigned_command包含command、签名类型和签名字符串。command可以是op_codeInitCommand或者ResetCommand。通过nrfutil生成的Packet包含的是signed_command该command是InitCommand
protobuf github下载protoc for win32工具。
用nano-pb的generateor生成.c 和.h 文件

D:\ble\nrf52832\nRF5_SDK_12.2.0\external\nano-pb\generator>protoc -odfu_cc.pb dfu-cc.proto
[libprotobuf WARNING google/protobuf/compiler/parser.cc:546] No syntax specified
 for the proto file: dfu-cc.proto. Please use 'syntax = "proto2";' or 'syntax ="proto3";' to specify a syntax version. (Defaulted to proto2 syntax.)

D:\ble\nrf52832\nRF5_SDK_12.2.0\external\nano-pb\generator>nanopb_generator.py dfu_cc.pb
Writing to dfu_cc.pb.h and dfu_cc.pb.c

生成的代码如下dfu_cc.pb.h:

/* Automatically generated nanopb header */
/* Generated by nanopb-0.3.6-dev at Thu Dec 29 18:01:42 2016. */

#ifndef PB_DFU_CC_PB_H_INCLUDED
#define PB_DFU_CC_PB_H_INCLUDED
#include <pb.h>

/* @@protoc_insertion_point(includes) */
#if PB_PROTO_HEADER_VERSION != 30
#error Regenerate this file with the current version of nanopb generator.
#endif

#ifdef __cplusplus
extern "C" {
#endif

/* Enum definitions */
typedef enum
{
    DFU_OP_CODE_RESET = 0,
    DFU_OP_CODE_INIT = 1
} dfu_op_code_t;
#define DFU_OP_CODE_MIN DFU_OP_CODE_RESET
#define DFU_OP_CODE_MAX DFU_OP_CODE_INIT
#define DFU_OP_CODE_ARRAYSIZE ((dfu_op_code_t)(DFU_OP_CODE_INIT+1))

typedef enum
{
    DFU_FW_TYPE_APPLICATION = 0,
    DFU_FW_TYPE_SOFTDEVICE = 1,
    DFU_FW_TYPE_BOOTLOADER = 2,
    DFU_FW_TYPE_SOFTDEVICE_BOOTLOADER = 3
} dfu_fw_type_t;
#define DFU_FW_TYPE_MIN DFU_FW_TYPE_APPLICATION
#define DFU_FW_TYPE_MAX DFU_FW_TYPE_SOFTDEVICE_BOOTLOADER
#define DFU_FW_TYPE_ARRAYSIZE ((dfu_fw_type_t)(DFU_FW_TYPE_SOFTDEVICE_BOOTLOADER+1))

typedef enum
{
    DFU_HASH_TYPE_NO_HASH = 0,
    DFU_HASH_TYPE_CRC = 1,
    DFU_HASH_TYPE_SHA128 = 2,
    DFU_HASH_TYPE_SHA256 = 3,
    DFU_HASH_TYPE_SHA512 = 4
} dfu_hash_type_t;
#define DFU_HASH_TYPE_MIN DFU_HASH_TYPE_NO_HASH
#define DFU_HASH_TYPE_MAX DFU_HASH_TYPE_SHA512
#define DFU_HASH_TYPE_ARRAYSIZE ((dfu_hash_type_t)(DFU_HASH_TYPE_SHA512+1))

typedef enum
{
    DFU_SIGNATURE_TYPE_ECDSA_P256_SHA256 = 0,
    DFU_SIGNATURE_TYPE_ED25519 = 1
} dfu_signature_type_t;
....

自动生成的dfu_cc.pb.c如下:

/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.3.6-dev at Thu Dec 29 18:01:42 2016. */

#include "dfu_cc.pb.h"

/* @@protoc_insertion_point(includes) */
#if PB_PROTO_HEADER_VERSION != 30
#error Regenerate this file with the current version of nanopb generator.
#endif

const bool dfu_init_command_is_debug_default = false;


const pb_field_t dfu_hash_fields[3] = {
    PB_FIELD(  1, UENUM   , REQUIRED, STATIC  , FIRST, dfu_hash_t, hash_type, hash_type, 0),
    PB_FIELD(  2, BYTES   , REQUIRED, CALLBACK, OTHER, dfu_hash_t, hash, hash_type, 0),
    PB_LAST_FIELD
};

const pb_field_t dfu_init_command_fields[10] = {
    PB_FIELD(  1, UINT32  , OPTIONAL, STATIC  , FIRST, dfu_init_command_t, fw_version, fw_version, 0),
    PB_FIELD(  2, UINT32  , OPTIONAL, STATIC  , OTHER, dfu_init_command_t, hw_version, fw_version, 0),
    PB_FIELD(  3, UINT32  , REPEATED, CALLBACK, OTHER, dfu_init_command_t, sd_req, hw_version, 0),
    PB_FIELD(  4, UENUM   , OPTIONAL, STATIC  , OTHER, dfu_init_command_t, type, sd_req, 0),
    PB_FIELD(  5, UINT32  , OPTIONAL, STATIC  , OTHER, dfu_init_command_t, sd_size, type, 0),
    PB_FIELD(  6, UINT32  , OPTIONAL, STATIC  , OTHER, dfu_init_command_t, bl_size, sd_size, 0),
    PB_FIELD(  7, UINT32  , OPTIONAL, STATIC  , OTHER, dfu_init_command_t, app_size, bl_size, 0),
    PB_FIELD(  8, MESSAGE , OPTIONAL, STATIC  , OTHER, dfu_init_command_t, hash, app_size, &dfu_hash_fields),
    PB_FIELD(  9, BOOL    , OPTIONAL, STATIC  , OTHER, dfu_init_command_t, is_debug, hash, &dfu_init_command_is_debug_default),
    PB_LAST_FIELD
};

const pb_field_t dfu_reset_command_fields[2] = {
    PB_FIELD(  1, UINT32  , REQUIRED, STATIC  , FIRST, dfu_reset_command_t, timeout, timeout, 0),
    PB_LAST_FIELD
};

const pb_field_t dfu_command_fields[4] = {
    PB_FIELD(  1, UENUM   , OPTIONAL, STATIC  , FIRST, dfu_command_t, op_code, op_code, 0),
    PB_FIELD(  2, MESSAGE , OPTIONAL, STATIC  , OTHER, dfu_command_t, init, op_code, &dfu_init_command_fields),
    PB_FIELD(  3, MESSAGE , OPTIONAL, STATIC  , OTHER, dfu_command_t, reset, init, &dfu_reset_command_fields),
    PB_LAST_FIELD
};

const pb_field_t dfu_signed_command_fields[4] = {
    PB_FIELD(  1, MESSAGE , REQUIRED, STATIC  , FIRST, dfu_signed_command_t, command, command, &dfu_command_fields),
    PB_FIELD(  2, UENUM   , REQUIRED, STATIC  , OTHER, dfu_signed_command_t, signature_type, command, 0),
    PB_FIELD(  3, BYTES   , REQUIRED, CALLBACK, OTHER, dfu_signed_command_t, signature, signature_type, 0),
    PB_LAST_FIELD
};

const pb_field_t dfu_packet_fields[3] = {
    PB_FIELD(  1, MESSAGE , OPTIONAL, STATIC  , FIRST, dfu_packet_t, command, command, &dfu_command_fields),
    PB_FIELD(  2, MESSAGE , OPTIONAL, STATIC  , OTHER, dfu_packet_t, signed_command, command, &dfu_signed_command_fields),
    PB_LAST_FIELD
};
....

NRF52832 dfu 代码分析

Title: Packet decode过程
on_ble_evt->on_ctrl_pt_write:ble 接收到数据包
on_ctrl_pt_write->nrf_dfu_req_handler_on_req:处理请求
nrf_dfu_req_handler_on_req->nrf_dfu_command_req:执行命令
nrf_dfu_command_req->dfu_decode_commmand:调用nano pb解码

dfu_decode_command函数,通过调用nano-pb的pb_decode来解码packet。

static uint32_t dfu_decode_commmand(void)
{
    stream = pb_istream_from_buffer(s_dfu_settings.init_command, s_dfu_settings.progress.command_size);

    // Attach our callback to follow the field decoding
    stream.decoding_callback = pb_decoding_callback;
    // reset the variable where the init pointer and length will be stored.
    hash_data.p_le_data = NULL;
    hash_data.len = 0;

    if (!pb_decode(&stream, dfu_packet_fields, &packet))
    {
        NRF_LOG_INFO("Handler: Invalid protocol buffer stream\r\n");
        return 0;
    }

    return 1;
}

上述代码中stream.decoding_callback的用途。Packet中包含了init_command的内容也包含了init_commandECDSA签名,该签名是对init_command的hash值进行的签名。而本callback的目的是求得init_command的起始地址和长度。然后利用起始地址和长度计算hash值。在利用该hash值进行ECDSA签名验证。目前发现在新版本的nano-pb中已经不再支持callback这种方法。
init_command的hash算法采用sha256

static void pb_decoding_callback(pb_istream_t *str, uint32_t tag, pb_wire_type_t wire_type, void *iter)
{
    pb_field_iter_t* p_iter = (pb_field_iter_t *) iter;

    // match the beginning of the init command
    if(p_iter->pos->ptr == &dfu_init_command_fields[0])
    {
        uint8_t *ptr = (uint8_t *) str->state;
        uint32_t size = str->bytes_left;

        // remove tag byte
        ptr++;
        size--;

        // store the info in hash_data
        hash_data.p_le_data = ptr;
        hash_data.len = size;

        NRF_LOG_INFO("PB: Init data len: %d\r\n", hash_data.len);
    }
}

stm32平台上移植nano-pb并进行测试:
解压缩用nrfutil打包的zip文件。

2016/12/29  13:12            49,844 app.bin
2016/12/29  13:12               135 app.dat
2016/12/29  13:12               138 manifest.json

其中app.dat是protocol buffers格式的Packet。用Bin2C.exe工具将其转为.c文件:

/*
  C-file generated by Bin2C
  Compiled:    Jul  1 2015 at 10:51:35

  Copyright (C) 2014
  Segger Microcontroller GmbH & Co. KG
  www.segger.com

  Solutions for real time microcontroller applications
*/

static const unsigned char _acapp[135UL + 1] = {
  0x12, 0x84, 0x01, 0x0A, 0x3E, 0x08, 0x01, 0x12, 0x3A, 0x08, 0x04, 0x10, 0x52, 0x1A, 0x02, 0x8C, 0x01, 0x20, 0x00, 0x28, 0x00, 0x30, 0x00, 0x38, 0xB4, 0x85, 0x03, 0x42, 0x24, 0x08, 0x03, 0x12, 0x20, 0xD7, 0xFE, 0x48, 0x42, 0x19, 0x5F, 0x7B,
  0x91, 0x27, 0xD4, 0xB5, 0x22, 0xEE, 0x5D, 0x6B, 0x4E, 0xF3, 0xE7, 0x55, 0x67, 0x75, 0x08, 0x09, 0x3C, 0x7F, 0x0E, 0xD2, 0x92, 0x49, 0xF0, 0xC1, 0x54, 0x48, 0x00, 0x10, 0x00, 0x1A, 0x40, 0x08, 0xB9, 0x28, 0xA1, 0x65, 0x65, 0x90, 0x80, 0x28,
  0x07, 0x40, 0x5C, 0x37, 0x52, 0x2E, 0xB2, 0xC1, 0x1B, 0x67, 0xFF, 0x6B, 0x56, 0x88, 0x51, 0x7D, 0x05, 0x2C, 0xEF, 0xCB, 0x8C, 0x8F, 0xBB, 0xD1, 0x0E, 0xAF, 0x3F, 0x5F, 0x7B, 0x6A, 0xFA, 0xB1, 0xC7, 0xC2, 0xBF, 0x21, 0x80, 0x7F, 0x93, 0xC9,
  0x1D, 0xD1, 0xC9, 0x40, 0x6B, 0xA0, 0x36, 0xE6, 0x2E, 0x99, 0x23, 0xC3, 0xC0, 0x3D, 0x93, 0x00
};

/*************************** End of file ****************************/

利用STM32F746 Discovery的板子,在已有工程上添加nano-pb所需要的文件。
image_1b5hl2uet1o6d4gb4jth11i2m9.png-139.5kB
在写如下测试代码:

static const unsigned char dfu_cc_packet[135UL + 1] = {
  0x12, 0x84, 0x01, 0x0A, 0x3E, 0x08, 0x01, 0x12, 0x3A, 0x08, 0x04, 0x10, 0x52, 0x1A, 0x02, 0x8C, 0x01, 0x20, 0x00, 0x28, 0x00, 0x30, 0x00, 0x38, 0xB4, 0x85, 0x03, 0x42, 0x24, 0x08, 0x03, 0x12, 0x20, 0xD7, 0xFE, 0x48, 0x42, 0x19, 0x5F, 0x7B,
  0x91, 0x27, 0xD4, 0xB5, 0x22, 0xEE, 0x5D, 0x6B, 0x4E, 0xF3, 0xE7, 0x55, 0x67, 0x75, 0x08, 0x09, 0x3C, 0x7F, 0x0E, 0xD2, 0x92, 0x49, 0xF0, 0xC1, 0x54, 0x48, 0x00, 0x10, 0x00, 0x1A, 0x40, 0x08, 0xB9, 0x28, 0xA1, 0x65, 0x65, 0x90, 0x80, 0x28,
  0x07, 0x40, 0x5C, 0x37, 0x52, 0x2E, 0xB2, 0xC1, 0x1B, 0x67, 0xFF, 0x6B, 0x56, 0x88, 0x51, 0x7D, 0x05, 0x2C, 0xEF, 0xCB, 0x8C, 0x8F, 0xBB, 0xD1, 0x0E, 0xAF, 0x3F, 0x5F, 0x7B, 0x6A, 0xFA, 0xB1, 0xC7, 0xC2, 0xBF, 0x21, 0x80, 0x7F, 0x93, 0xC9,
  0x1D, 0xD1, 0xC9, 0x40, 0x6B, 0xA0, 0x36, 0xE6, 0x2E, 0x99, 0x23, 0xC3, 0xC0, 0x3D, 0x93, 0x00
};

static void pb_decoding_callback(pb_istream_t *str, uint32_t tag, pb_wire_type_t wire_type, void *iter)
{
    pb_field_iter_t* p_iter = (pb_field_iter_t *) iter;
	rtt_printf("%s\r\n",__FUNCTION__);
    // match the beginning of the init command
    if(p_iter->pos->ptr == &dfu_init_command_fields[0])
    {
        uint8_t *ptr = (uint8_t *) str->state;
        uint32_t size = str->bytes_left;
        // remove tag byte
        ptr++;
        size--;
        // store the info in hash_data
       rtt_printf("PB: Init data len: 0x%x,%d\r\n", ptr,size);
    }
    
}

pb_istream_t stream;
dfu_packet_t packet = DFU_PACKET_INIT_DEFAULT;

void test_pb(void)
{
    stream = pb_istream_from_buffer(dfu_cc_packet, 136);

    // Attach our callback to follow the field decoding
    stream.decoding_callback = pb_decoding_callback;
    // reset the variable where the init pointer and length will be stored.

    if (!pb_decode(&stream, dfu_packet_fields, &packet))
    {
        rtt_printf("Handler: Invalid protocol buffer stream\r\n");
        return;
    }
	 rtt_printf("valid protocol buffer stream\r\n");
    return;

}
#endif 

运算后的packet结构如下:
image_1b5hljlkafhi1km4vu2aqo1knl9.png-159.7kB

duf_secure代码解析

###memory map

项目开始地址结束地址长度
MBR0x00000x10000x1000
softdevice0x10000x1f0000x1e000(120K)
application0x1f0000x780000x59000(356K)
dfu bootloader0x780000x7e0000x6000
bootloader setting0x7f0000x800000x1000
MBR parameters0x7e0000x7f0000x1000

###bootloader setting

/**@brief DFU settings for application and bank data.
 */
typedef struct
{
    uint32_t            crc;                /**< CRC for the stored DFU settings, not including the CRC itself. If 0xFFFFFFF, the CRC has never been calculated. */
    uint32_t            settings_version;   /**< Version of the currect dfu settings struct layout.*/
    uint32_t            app_version;        /**< Version of the last stored application. */
    uint32_t            bootloader_version; /**< Version of the last stored bootloader. */

    uint32_t            bank_layout;        /**< Bank layout: single bank or dual bank. This value can change. */
    uint32_t            bank_current;       /**< The bank that is currently used. */

    nrf_dfu_bank_t      bank_0;             /**< Bank 0. */
    nrf_dfu_bank_t      bank_1;             /**< Bank 1. */

    uint32_t            write_offset;       /**< Write offset for the current operation. */
    uint32_t            sd_size;            /**< SoftDevice size (if combined BL and SD). */

    dfu_progress_t      progress;           /**< Current DFU progress. */

    uint32_t            enter_buttonless_dfu;
    uint8_t             init_command[INIT_COMMAND_MAX_SIZE];  /**< Buffer for storing the init command. */
} nrf_dfu_settings_t;

###bootloader 检查升级包的有效性
bootloader 通过dfu_handler_prevalidate来检查升级包的有效性。会根据nrfutil打包升级包时输入的--hw-version,--sd-req,--applicatin-version 来判断升级包的有效性。同时还会验证ECDSA签名。

static nrf_dfu_res_code_t dfu_handle_prevalidate(dfu_signed_command_t const * p_command, pb_istream_t * p_stream, uint8_t * p_init_cmd, uint32_t init_cmd_len)

soft device 通过判断UICR[User information configuration registers] 写入的bootload_start_address来判断是否有dfu bootloader。

#if defined (__CC_ARM )
    #pragma push
    #pragma diag_suppress 1296
    uint32_t  m_uicr_bootloader_start_address __attribute__((at(NRF_UICR_BOOTLOADER_START_ADDRESS)))
                                                    = BOOTLOADER_START_ADDR;
    #pragma pop
#elif defined ( __GNUC__ )
    volatile uint32_t m_uicr_bootloader_start_address  __attribute__ ((section(".uicrBootStartAddress")))
                                            = BOOTLOADER_START_ADDR;
#elif defined ( __ICCARM__ )
    __root    const uint32_t m_uicr_bootloader_start_address @ NRF_UICR_BOOTLOADER_START_ADDRESS
                                            = BOOTLOADER_START_ADDR;
#endif
nRF52832串口DFU是指使用Nordic公司为其nRF52832芯片提供的串口(USART)接口进行固件升级的一种方法。串口DFU代表数据固件升级(Data Firmware Update),通过串口接口将新的固件传输到nRF52832芯片中,实现固件升级的功能。 nRF52832是一款低功耗、高集成度的蓝牙低功耗(BLE)系统级芯片,适用于物联网和低功耗应用。在开发过程中,固件升级是一个重要的环节,可以使产品保持最新的功能和性能,修复已知的漏洞和错误。 nRF52832串口DFU的工作原理是通过将新的固件文件传输到nRF52832芯片的内部存储器进行更新。首先,在计算机上安装nRF52832串口DFU工具,并连接串口线将计算机与nRF52832芯片连接起来。然后,选择要升级的固件文件,并发送到串口接口。nRF52832芯片会接收到固件文件并将其存储到内部存储器中。最后,芯片重启并加载新的固件,完成固件升级过程。 使用nRF52832串口DFU进行固件升级具有很多优点。首先,它可以通过串口线直接从计算机传输固件文件,无需额外的硬件设备。其次,升级过程简单方便,不需要拆卸设备或更换芯片。此外,nRF52832芯片的低功耗特性使得固件升级过程中能够保持设备的低功耗工作状态。 总的来说,nRF52832串口DFU是一种方便、高效的固件升级方法,可以帮助开发人员和制造商保持产品的最新性能和功能,并提供更好的用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值