ChipWhisperer教程(三)

——CW305目标板的波形采集

一、目标板介绍

CW305 是一款独立的 FPGA 目标板,搭载的FPGA芯片为Xilinx Artix-7系列。 它具有与 FPGA 通信的 USB 接口、为 FPGA 提供时钟的外部 PLL、编程 VCC-INT 电源以及用于故障注入环境的二极管保护。 CW305 电路板有多种配置,与其他 ChipWhisperer 独立目标一样,它需要外部设备进行侧通道功率分析或故障注入,并具有标准 ChipWhisperer 20 引脚/SMA 接口。

Xilinx Artix-7系列是赛灵思公司推出的一款中高端现场可编程门阵列(FPGA),它主要面向需要较高性能和较低成本的设计应用。以下是Xilinx Artix-7系列的一些主要特点:

  1. 逻辑资源丰富:Artix-7系列提供了大量的逻辑单元、查找表(LUTs)、寄存器、块RAM和数字信号处理(DSP)切片,可以满足复杂算法和数据处理的需求。
  2. 高速IO:支持高速串行IO标准,如6.25 Gbps的GTP收发器,能够实现高速数据传输。
  3. 集成硬核处理器:部分Artix-7器件集成了硬核处理器,如PowerPC 440,可用于处理复杂控制任务。
  4. 高级时钟管理:具有多个数字时钟管理器(DCMs)和锁相环(PLLs),可以提供稳定的时钟信号,支持多种时钟域的操作。

二、目标板代码编写

CW305目标板的代码为verilog代码,通过vivado软件进行综合、实现和生成比特流文件。CW305目标板通过USB接口与FPGA芯片通信,目标板代码同样分为USB接口通信和密码算法实现两部分,密码算法实现本文不再讲解,读者自行学习。ChiWhisperer官网已经给出了AES循环迭代实现、AES流水线实现和ECC的代码,以及与CW305、CW312T-A35、CW312T-iCE40和CW308T-S6LX9共4种FPGA目标板的通信接口(参考https://github.com/newaetech/chipwhisperer/tree/develop/firmware/fpgas​)。以CW305的AES循环迭代实现为例,涉及CW305的USB通信接口的代码包含以下6个verilog文件:

  • cdc_pulse.v:不同时钟域的同步。
  • clocks.v:选择输入时钟并驱动输出时钟。
  • cw305_usb_reg_fe.v:处理USB接口的前端逻辑。
  • cw305_defines.v:定义了一些寄存器地址(常量),用于在系统中进行寄存器访问。
  • cw305_reg_aes.v:处理与AES加密相关的寄存器操作。
  • cw305_top.v:顶层模块,连接了各个子模块并处理系统的整体逻辑。

前三个文件相对固定,一般无需修改,本文主要对后面三个文件进行说明。

1. cw305_defines.v

该文件定义了一些寄存器的地址,如果用户有新的需求可以添加更多的地址,默认14位的地址空间,比较充足。

`define REG_CLKSETTINGS                 'h00	//密码运算单元的时钟来源
`define REG_USER_LED                    'h01	//CW305目标板上的用户LED灯
`define REG_CRYPT_TYPE                  'h02	//加密类型
`define REG_CRYPT_REV                   'h03	//加密版本
`define REG_IDENTIFY                    'h04	//标识符
`define REG_CRYPT_GO                    'h05	//写该地址开始执行加/解密和采波,读该地址获取加密运算单元是否繁忙
`define REG_CRYPT_TEXTIN                'h06	//输出到加密运算单元的明文
`define REG_CRYPT_CIPHERIN              'h07	//输出到加密运算单元的密文
`define REG_CRYPT_TEXTOUT               'h08	//来自加密运算单元的明文
`define REG_CRYPT_CIPHEROUT             'h09	//来自加密运算单元的明文
`define REG_CRYPT_KEY                   'h0a	//密钥
`define REG_BUILDTIME                   'h0b	//构建时间

2. cw305_reg_aes.v

`default_nettype none		//设置没有显示声明的网络类型为none,编译器会报错,用于捕获未声明的信号
`timescale 1ns / 1ps		//设置仿真时间单位为1ns,精度为1ps
`include "cw305_defines.v"	//包含cw305_defines.v文件

//模块声明和参数化
module cw305_reg_aes #(
   parameter pADDR_WIDTH = 21,	//寄存器地址宽度
   parameter pBYTECNT_SIZE = 7,	//对寄存器进行字节计数的大小
   parameter pDONE_EDGE_SENSITIVE = 1,	//指示I_done信号是否是边沿敏感的
   parameter pPT_WIDTH = 128,	//明文宽度
   parameter pCT_WIDTH = 128,	//密文宽度
   parameter pKEY_WIDTH = 128,	//密钥宽度
   parameter pCRYPT_TYPE = 2,	//加密类型
   parameter pCRYPT_REV = 4,	//加密版本
   parameter pIDENTIFY = 8'h2e	//标识符
)(
	//主机与寄存器交互的端口声明
   input  wire                                  usb_clk,		//USB时钟
   input  wire                                  crypto_clk,		//加密时钟
   input  wire                                  reset_i,		//复位信号
   input  wire [pADDR_WIDTH-pBYTECNT_SIZE-1:0]  reg_address,	//寄存器地址
   input  wire [pBYTECNT_SIZE-1:0]              reg_bytecnt,	//寄存器中第几个字节
   output reg  [7:0]                            read_data,		//从寄存器读取的数据,将会通过USB接口传回主机
   input  wire [7:0]                            write_data,		//从主机传入的用于写入寄存器的数据
   input  wire                                  reg_read,		//读有效标志
   input  wire                                  reg_write,		//写有效标志
   input  wire                                  reg_addrvalid,	//地址有效标志
   input  wire                                  exttrigger_in,	//外部触发信号,为高则开始加密和采波

	//来自加密运算单元的输入信号
   input  wire [pPT_WIDTH-1:0]                  I_textout,		//明文(执行解密时有效)
   input  wire [pCT_WIDTH-1:0]                  I_cipherout,	//密文(执行加密时有效)
   input  wire                                  I_ready,		//准备就绪
   input  wire                                  I_done,			//执行完毕
   input  wire                                  I_busy,			//执行中

	//输出到加密运算单元的信号
   output reg  [4:0]                            O_clksettings,	//用于选择加密运算单元的时钟信号
   output reg                                   O_user_led,		//用户LED灯信号
   output wire [pKEY_WIDTH-1:0]                 O_key,			//密钥
   output wire [pPT_WIDTH-1:0]                  O_textin,		//明文(执行加密时有效)
   output wire [pCT_WIDTH-1:0]                  O_cipherin,		//密文(执行解密时有效)
   output wire                                  O_start			//开始执行加/解密并采波

);
	//内部信号声明
   reg  [7:0]                   reg_read_data;			//存储从USB接口读取的数据
   reg  [pCT_WIDTH-1:0]         reg_crypt_cipherin;		//输出到加密运算单元的密文
   reg  [pKEY_WIDTH-1:0]        reg_crypt_key;			//密钥
   reg  [pPT_WIDTH-1:0]         reg_crypt_textin;		//输出到加密运算单元的明文
   reg  [pPT_WIDTH-1:0]         reg_crypt_textout;		//来自加密运算单元的明文
   reg  [pCT_WIDTH-1:0]         reg_crypt_cipherout;	//来自加密运算单元的密文
   reg                          reg_crypt_go_pulse;		//用于生成加密启动脉冲
   wire                         reg_crypt_go_pulse_crypt;//加密启动脉冲1

	//状态管理和信号同步
   reg                          busy_usb;
   reg                          done_r;
   wire                         done_pulse;
   wire                         crypt_go_pulse;		//加密启动脉冲2
   reg                          go_r;
   reg                          go;
   wire [31:0]                  buildtime;			//保存构建时间

	//ASYNC_REG属性用于跨时钟域的信号同步
   (* ASYNC_REG = "TRUE" *) reg  [pKEY_WIDTH-1:0] reg_crypt_key_crypt;
   (* ASYNC_REG = "TRUE" *) reg  [pPT_WIDTH-1:0] reg_crypt_textin_crypt;
   (* ASYNC_REG = "TRUE" *) reg  [pPT_WIDTH-1:0] reg_crypt_textout_usb;
   (* ASYNC_REG = "TRUE" *) reg  [pCT_WIDTH-1:0] reg_crypt_cipherout_usb;
   (* ASYNC_REG = "TRUE" *) reg  [1:0] go_pipe;
   (* ASYNC_REG = "TRUE" *) reg  [1:0] busy_pipe;

	//时钟域同步逻辑
	//在crypto_clk时钟域中,处理I_done信号的边沿检测,并生成done_pulse信号
   always @(posedge crypto_clk) begin
       done_r <= I_done & pDONE_EDGE_SENSITIVE;
   end
   assign done_pulse = I_done & ~done_r;

	//在crypto_clk时钟域中,将加密完成时的密文和明文数据存储到reg_crypt_cipherout和reg_crypt_textout中
   always @(posedge crypto_clk) begin
       if (done_pulse) begin
           reg_crypt_cipherout <= I_cipherout;
           reg_crypt_textout   <= I_textout;
       end
       reg_crypt_key_crypt <= reg_crypt_key;
       reg_crypt_textin_crypt <= reg_crypt_textin;
   end

	//在usb_clk时钟域中,将reg_crypt_cipherout和reg_crypt_textout同步到usb_clk时钟域
   always @(posedge usb_clk) begin
       reg_crypt_cipherout_usb <= reg_crypt_cipherout;
       reg_crypt_textout_usb   <= reg_crypt_textout;
   end

	//将同步后的信号作为密码运算单元的输入
   assign O_textin = reg_crypt_textin_crypt;
   assign O_key = reg_crypt_key_crypt;
   assign O_start = crypt_go_pulse || reg_crypt_go_pulse_crypt;

	//读寄存器逻辑,读取结果会通过USB接口传回电脑主机
   always @(*) begin
      if (reg_addrvalid && reg_read) begin //当reg_addrvalid和reg_read为高电平时开始读寄存器
         case (reg_address)	//根据reg_address选择读取的数据是什么
            `REG_CLKSETTINGS:           reg_read_data = O_clksettings;	//读加密时钟设置寄存器
            `REG_USER_LED:              reg_read_data = O_user_led;		//读用户LED灯寄存器
            `REG_CRYPT_TYPE:            reg_read_data = pCRYPT_TYPE;	//读加密类型
            `REG_CRYPT_REV:             reg_read_data = pCRYPT_REV;		//读加密
            `REG_IDENTIFY:              reg_read_data = pIDENTIFY;		//读标识符
            `REG_CRYPT_GO:              reg_read_data = busy_usb;		//读加密运算单元是否繁忙

			//reg_bytecnt在此处指明读取128位数据中的哪个字节
			//读密钥寄存器
            `REG_CRYPT_KEY:             reg_read_data = reg_crypt_key[reg_bytecnt*8 +: 8];
			//读输出到加密运算单元的明文寄存器
            `REG_CRYPT_TEXTIN:          reg_read_data = reg_crypt_textin[reg_bytecnt*8 +: 8];
			//读输出到加密运算单元的密文寄存器
            `REG_CRYPT_CIPHERIN:        reg_read_data = reg_crypt_cipherin[reg_bytecnt*8 +: 8];
			//读来自加密运算单元的明文寄存器
            `REG_CRYPT_TEXTOUT:         reg_read_data = reg_crypt_textout_usb[reg_bytecnt*8 +: 8];
			//读来自加密运算单元的密文寄存器
            `REG_CRYPT_CIPHEROUT:       reg_read_data = reg_crypt_cipherout_usb[reg_bytecnt*8 +: 8];
			//读构建时间寄存器
            `REG_BUILDTIME:             reg_read_data = buildtime[reg_bytecnt*8 +: 8];
            default:                    reg_read_data = 0;
         endcase
      end
      else
         reg_read_data = 0;
   end

	//在usb_clk时钟域中,将reg_read_data赋值给read_data
   always @(posedge usb_clk)
      read_data <= reg_read_data;

	//写寄存器逻辑,将来自电脑主机的数据写入寄存器
   always @(posedge usb_clk) begin
      if (reset_i) begin
         O_clksettings <= 0;
         O_user_led <= 0;
         reg_crypt_go_pulse <= 1'b0;
      end
      else begin
         if (reg_addrvalid && reg_write) begin	//当reg_addrvalid和reg_write为高电平时开始写寄存器
            case (reg_address)
               `REG_CLKSETTINGS:        O_clksettings <= write_data;	//写加密时钟设置寄存器
               `REG_USER_LED:           O_user_led <= write_data;		//写用户LED灯寄存器
				//写输出到加密运算单元的明文寄存器
               `REG_CRYPT_TEXTIN:       reg_crypt_textin[reg_bytecnt*8 +: 8] <= write_data;
				//写输出到加密运算单元的密文寄存器
               `REG_CRYPT_CIPHERIN:     reg_crypt_cipherin[reg_bytecnt*8 +: 8] <= write_data;
				//写密钥寄存器
               `REG_CRYPT_KEY:          reg_crypt_key[reg_bytecnt*8 +: 8] <= write_data;
            endcase
         end
         //写入REG_CRYPT_GO寄存器会生成一个脉冲信号,用于启动加/解密和采波过程
         if ( (reg_addrvalid && reg_write && (reg_address == `REG_CRYPT_GO)) )
            reg_crypt_go_pulse <= 1'b1;
         else
            reg_crypt_go_pulse <= 1'b0;
      end
   end

	//跨时钟域同步逻辑
	//在crypto_clk时钟域中,处理exttrigger_in信号的同步,并生成crypt_go_pulse信号
   always @(posedge crypto_clk) begin
      {go_r, go, go_pipe} <= {go, go_pipe, exttrigger_in};
   end
   assign crypt_go_pulse = go & !go_r;

	//使用cdc_pulse模块将reg_crypt_go_pulse信号从usb_clk时钟域同步到crypto_clk时钟域
   cdc_pulse U_go_pulse (
      .reset_i       (reset_i),
      .src_clk       (usb_clk),
      .src_pulse     (reg_crypt_go_pulse),
      .dst_clk       (crypto_clk),
      .dst_pulse     (reg_crypt_go_pulse_crypt)
   );

	//在usb_clk时钟域中,将I_busy信号同步到busy_usb
   always @(posedge usb_clk)
      {busy_usb, busy_pipe} <= {busy_pipe, I_busy};

	//如果没有定义__ICARUS__宏,则使用USR_ACCESSE2原语读取构建时间
   `ifndef __ICARUS__
      USR_ACCESSE2 U_buildtime (
         .CFGCLK(),
         .DATA(buildtime),
         .DATAVALID()
      );
   `else	//如果定义了__ICARUS__宏,则将buildtime信号赋值为0
      assign buildtime = 0;
   `endif

endmodule

`default_nettype wire	//恢复默认的网络类型为wire

如果密码运算单元还需要输入其他数据,则需要新定义一个寄存器,添加写该寄存器的逻辑,并将寄存器值经过同步后输出。如果密码运算单元会产生更多的输出,同样先新定义一个寄存器,在crypt_clk时钟域获取密码运算单元的输出,之后将寄存器的值同步到usb_clk时钟域,并添加读该寄存器的逻辑。最后,不要忘记在cw305_defines.v中添加寄存器地址的常量。

3. cw305_top.v

`timescale 1ns / 1ps	//设置仿真时间单位为1ns,精度为1ps
`default_nettype none	//设置没有显示声明的网络类型为none,编译器会报错,用于捕获未声明的信号

//模块声明和参数化
module cw305_top #(
    parameter pBYTECNT_SIZE = 7,	//对寄存器进行字节计数的大小
    parameter pADDR_WIDTH = 21,		//寄存器地址的宽度
    parameter pPT_WIDTH = 128,		//明文宽度
    parameter pCT_WIDTH = 128,		//密文宽度
    parameter pKEY_WIDTH = 128		//密钥宽度
)(
    //USB接口
    input wire                          usb_clk,        //USB时钟
    inout wire [7:0]                    usb_data,       //USB数据,用于读写操作
    input wire [pADDR_WIDTH-1:0]        usb_addr,       //USB地址
    input wire                          usb_rdn,        //USB读信号
    input wire                          usb_wrn,        //USB写信号
    input wire                          usb_cen,        //USB片选信号
    input wire                          usb_trigger,    //触发信号

    //CW305目标板上DIP开关、按钮和LED灯
    input wire                          j16_sel,        //DIP开关输入
    input wire                          k16_sel,        //DIP开关输入
    input wire                          k15_sel,        //DIP开关输入
    input wire                          l14_sel,        //DIP开关输入
    input wire                          pushbutton,     //复位按钮
    output wire                         led1,           //红色LED输出
    output wire                         led2,           //绿色LED输出
    output wire                         led3,           //蓝色LED输出

    //PLL
    input wire                          pll_clk1,       //PLL时钟通道1
    //input wire                        pll_clk2,       //PLL时钟通道2(本例中未使用)

    //20针连接器信号
    output wire                         tio_trigger,	//输出到外部的触发信号(本例为密码运算单元繁忙信号)
    output wire                         tio_clkout,		//输出到外部的密码运算单元时钟
    input  wire                         tio_clkin		//外部时钟
    );

    wire [pKEY_WIDTH-1:0] crypt_key;		//密钥
    wire [pPT_WIDTH-1:0] crypt_textout;		//加密运算单元的输入明文
    wire [pCT_WIDTH-1:0] crypt_cipherin;	//加密运算单元的输出密文

	//加密状态信号
	wire crypt_ready;
    wire crypt_start;
    wire crypt_done;
    wire crypt_busy;

	//USB和寄存器相关的信号
    wire usb_clk_buf;
    wire [7:0] usb_dout;
    wire isout;
    wire [pADDR_WIDTH-pBYTECNT_SIZE-1:0] reg_address;
    wire [pBYTECNT_SIZE-1:0] reg_bytecnt;
    wire reg_addrvalid;
    wire [7:0] write_data;
    wire [7:0] read_data;
    wire reg_read;
    wire reg_write;
    wire [4:0] clk_settings;
    wire crypt_clk;  

	//复位信号
    wire resetn = pushbutton;
    wire reset = !resetn;


    //生成USB时钟的心跳信号(通过观察CW305目标板的红色LED灯是否闪烁来判断USB时钟是否正常)
    reg [24:0] usb_timer_heartbeat;
    always @(posedge usb_clk_buf) usb_timer_heartbeat <= usb_timer_heartbeat +  25'd1;
    assign led1 = usb_timer_heartbeat[24];

    //生成加密时钟的心跳信号(通过观察CW305目标板的绿色LED灯是否闪烁来判断加密时钟是否正常)
    reg [22:0] crypt_clk_heartbeat;
    always @(posedge crypt_clk) crypt_clk_heartbeat <= crypt_clk_heartbeat +  23'd1;
    assign led2 = crypt_clk_heartbeat[22];

	//实例化cw305_usb_reg_fe模块,处理USB接口的寄存器读写操作
    cw305_usb_reg_fe #(
       .pBYTECNT_SIZE           (pBYTECNT_SIZE),
       .pADDR_WIDTH             (pADDR_WIDTH)
    ) U_usb_reg_fe (
       .rst                     (reset),
       .usb_clk                 (usb_clk_buf), 
       .usb_din                 (usb_data), 
       .usb_dout                (usb_dout), 
       .usb_rdn                 (usb_rdn), 
       .usb_wrn                 (usb_wrn),
       .usb_cen                 (usb_cen),
       .usb_alen                (1'b0),                 // unused
       .usb_addr                (usb_addr),
       .usb_isout               (isout), 
       .reg_address             (reg_address), 
       .reg_bytecnt             (reg_bytecnt), 
       .reg_datao               (write_data), 
       .reg_datai               (read_data),
       .reg_read                (reg_read), 
       .reg_write               (reg_write), 
       .reg_addrvalid           (reg_addrvalid)
    );

	//AES寄存器模块实例化,处理与AES加密相关的寄存器操作
    cw305_reg_aes #(
       .pBYTECNT_SIZE           (pBYTECNT_SIZE),
       .pADDR_WIDTH             (pADDR_WIDTH),
       .pPT_WIDTH               (pPT_WIDTH),
       .pCT_WIDTH               (pCT_WIDTH),
       .pKEY_WIDTH              (pKEY_WIDTH)
    ) U_reg_aes (
       .reset_i                 (reset),
       .crypto_clk              (crypt_clk),
       .usb_clk                 (usb_clk_buf), 
       .reg_address             (reg_address[pADDR_WIDTH-pBYTECNT_SIZE-1:0]), 
       .reg_bytecnt             (reg_bytecnt), 
       .read_data               (read_data), 
       .write_data              (write_data),
       .reg_read                (reg_read), 
       .reg_write               (reg_write), 
       .reg_addrvalid           (reg_addrvalid),

       .exttrigger_in           (usb_trigger),

       .I_textout               (128'b0),
       .I_cipherout             (crypt_cipherin),
       .I_ready                 (crypt_ready),
       .I_done                  (crypt_done),
       .I_busy                  (crypt_busy),

       .O_clksettings           (clk_settings),
       .O_user_led              (led3),
       .O_key                   (crypt_key),
       .O_textin                (crypt_textout),
       .O_cipherin              (),
       .O_start                 (crypt_start)
    );

	//根据isout信号的值,控制usb_data总线的方向
    assign usb_data = isout? usb_dout : 8'bZ;

	//实例化clocks模块,处理时钟选择和输出
    clocks U_clocks (
       .usb_clk                 (usb_clk),
       .usb_clk_buf             (usb_clk_buf),
       .I_j16_sel               (j16_sel),
       .I_k16_sel               (k16_sel),
       .I_clock_reg             (clk_settings),
       .I_cw_clkin              (tio_clkin),
       .I_pll_clk1              (pll_clk1),
       .O_cw_clkout             (tio_clkout),
       .O_cryptoclk             (crypt_clk)
    );

	//AES运算单元输入输出信号定义
   wire aes_clk;
   wire [127:0] aes_key;
   wire [127:0] aes_pt;
   wire [127:0] aes_ct;
   wire aes_load;
   wire aes_busy;

   assign aes_clk = crypt_clk;
   assign aes_key = crypt_key;
   assign aes_pt = crypt_textout;
   assign crypt_cipherin = aes_ct;
   assign aes_load = crypt_start;
   assign crypt_ready = 1'b1;
   assign crypt_done = ~aes_busy;
   assign crypt_busy = aes_busy;

	//AES运算单元实例化
   aes_core (
        .clk(aes_clk),
		.load(aes_load),
	    .mkey(aes_key),
	    .pt(aes_pt),
	    .ct(aes_ct),
	    .busy(aes_busy)
   );

	//将aes_busy信号连接到tio_trigger输出端口
   assign tio_trigger = aes_busy;

endmodule

`default_nettype wire	//恢复默认的网络类型为wire

可见,这三个文件是相互配合的,编写分组密码算法时,不需要改动太多,只需要修改顶层模块,以及明密文的位宽参数。当编写更复杂的算法时,需要重新编写这三个文件,不过ChiWhipserer官方已经给出了AES流水线实现的ECC实现示例,用户可在此基础上进行修改。

三、采集代码编写

采集代码为运行在jupyter notebook上的代码,ChipWhipserer官方已经给了非常详细的说明,下载网址为https://github.com/newaetech/chipwhisperer-jupyter​。这里给一个比较标准的CW305目标板采集模板,并进行补充说明。

#连接捕获板,并进行一些初始设置(比较固定,一般不变)
import chipwhisperer as cw
scope = cw.scope()
scope.adc.basic_mode = "rising_edge"	#上升沿触发
scope.trigger.triggers = "tio4"			#触发信号为目标板20针SMA接口的IO4
scope.io.tio1 = "serial_rx"				#目标板20针SMA接口的IO1用于UART接收
scope.io.tio2 = "serial_tx"				#目标板20针SMA接口的IO2用于UART发送
scope.io.hs2 = "disabled"				#目标板20针SMA接口的HS2置为高阻态
#本文采用的是CW305-35t的目标板
TARGET_PLATFORM = 'CW305_35t'
fpga_id = '35t'

#比特流文件的路径
bit_file=r"D:\mycode\vivado\aes_normal\bitfile\cw305_top.bit"

#连接目标板,并将比特流文件烧写到FPGA
target = cw.target(scope, cw.targets.CW305, force=True, fpga_id=fpga_id,bsfile=bit_file)
#设置VCC-INT电压为1V,VCC-INT是内部逻辑电路的供电电压
target.vccint_set(1.0)
#仅启用PLL1,作为密码运行执行单元的时钟信号
target.pll.pll_enable_set(True)
target.pll.pll_outenable_set(False, 0)
target.pll.pll_outenable_set(True, 1)
target.pll.pll_outenable_set(False, 2)
#设置PLL1的频率为10MHz(可设置为5MHz-160MHz)
target.pll.pll_outfreq_set(10E6, 1)
#设置在加密运行单元执行过程中自动关闭USB时钟,以减少噪声
target.clkusbautooff = True
#设置USB时钟在任务完成后1毫秒进入睡眠模式
target.clksleeptime = 1

#确保ADC(模数转换器)的时钟源来自FPGA而不是内部时钟,可以提高数据采集的精度和稳定性
if scope._is_husky:
	scope.clock.clkgen_src = 'extclk'
else:
    scope.clock.adc_src = "extclk_x4"

#确保DCM(数字时钟管理器)锁定
import time
for i in range(5):
    scope.clock.reset_adc()
    time.sleep(0.5)
    if scope.clock.adc_locked:
        break 
assert (scope.clock.adc_locked), "ADC failed to lock"
#这里可以对一些捕获板的参数进行设置
scope.clock.adc_mul=10		#每个时钟周期采集的点数,默认为4
scope.gain.db=20			#低噪声放大器的增益,默认为25,当波形失真时可以减小该值,一般在[0,25]之间调整
scope.adc.samples = 1000	#采样点数
scope.adc.offset =0			#采样位置的偏移
'''
#波形的采集1
%matplotlib notebook
import matplotlib.pyplot as plt
import numpy as np
plaintext=bytearray([0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10])
key=bytearray([0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10])

#官方教程中均采用类似于下面的封装函数capture_trace进行采波
trs = cw.capture_trace(scope, target, plaintext, key)
print("plaintext:",trs.textin.hex())
print("key:",trs.key.hex())
print("ciphertext:",trs.textout.hex())
plt.plot(np.array(trs.wave))
plt.show()
'''
#波形的采集2
%matplotlib notebook
import matplotlib.pyplot as plt
import numpy as np
plaintext=bytearray([0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10])
key=bytearray([0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10])

#本文建设使用更底层的函数进行采集,以便理解如何对其他类型密码算法进行采波
#首先根据cw305_defines.v文件对target类中的寄存器地址进行设置(用户可根据实际情况设置更多的地址)
target.REG_CRYPT_KEY = 0x0a
target.REG_CRYPT_TEXTIN = 0x06
target.REG_CRYPT_GO = 0x05
target.REG_USER_LED = 0x01
target.REG_CRYPT_CIPHEROUT = 0x09
target.REG_BUILDTIME = 0x0b
target.REG_CRYPT_TYPE = 0x02
target.REG_CRYPT_REV = 0x03

#写入密钥,注意写入数据均需要先逆序
key = key[::-1]	#逆序操作
target.fpga_write(target.REG_CRYPT_KEY, key)

#写入明文
plaintext = plaintext[::-1]
target.fpga_write(target.REG_CRYPT_TEXTIN, plaintext)

#开始采波
scope.arm()
target.go()
ret = scope.capture()
if ret:
    print("Failed capture")
wave = scope.get_last_trace()

#读取密文
ciphertext = target.fpga_read(target.REG_CRYPT_CIPHEROUT, 16)
ciphertext = ciphertext[::-1]

#输出结果
print(ciphertext.hex())
plt.plot(np.array(wave))
plt.show()

从上面的程序可以看出,软件与硬件部分最关键的联系便是寄存器地址,这对于理解整个工作流程,以及扩展自己的密码算法比较重要。如果扩展密码算法比较复杂,可以参考ECC和AES流水线实现的示例,编写一个单独的采集脚本。

#开始正式采波

from tqdm.notebook import trange
import numpy as np

#可以根据上面的观察结果重新调整采集的点数和偏移
scope.adc.samples = 2000
scope.adc.offset =0

#根据cw305_defines.v文件对target类中的寄存器地址进行设置
target.REG_CRYPT_KEY = 0x0a
target.REG_CRYPT_TEXTIN = 0x06
target.REG_CRYPT_GO = 0x05
target.REG_USER_LED = 0x01
target.REG_CRYPT_CIPHEROUT = 0x09
target.REG_BUILDTIME = 0x0b
target.REG_CRYPT_TYPE = 0x02
target.REG_CRYPT_REV = 0x03

#采集60000条曲线
N=60000
plaintext=[]
ciphertext=[]
traces=[]

#写入密钥,注意写入数据均需要先逆序
key=bytearray([0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10])
key = key[::-1]	#逆序操作
target.fpga_write(target.REG_CRYPT_KEY, key)

#开始采集
for i in trange(N, desc='Capturing traces'):
    plain=np.random.randint(256,size=16,dtype=np.uint8)	#生成一个16字节随机明文
    plaintext.append(plain)
	plain= plain[::-1]
	target.fpga_write(target.REG_CRYPT_TEXTIN, plain)
    scope.arm()
	target.go()
	ret = scope.capture()
	if ret:
	    print("Failed capture")
	wave = scope.get_last_trace()
    traces.append(wave)
    cipher= target.fpga_read(target.REG_CRYPT_CIPHEROUT, 16)
	cipher = cipher[::-1]
    ciphertext.append(np.array(cipher,dtype=np.uint8))
#保存采集结果,建议均保存为.npy文件,读写速度较快,且可以直接读取为numpy数组,无需二次转化
#如果需要和其他非python程序交互,建议保存为二进制文件,如果保存为txt、csv等文本类型,则保存和读取速度均较慢

#保存能量曲线
traces_path="D:/WMD_DATA/aes_normal_traces.npy"
np.save(traces_path,np.array(traces))

#保存明文
plaintext_path="D:/WMD_DATA/aes_normal_plaintext.npy"
np.save(plaintext_path,np.array(plaintext,np.uint8))

#保存密文
ciphertext_path="D:/WMD_DATA/aes_normal_ciphertext.npy"
np.save(ciphertext_path,np.array(ciphertext,np.uint8))
#每次采集完毕记得断开和板子的连接
#如果不执行下面的函数,在其他jupyter notebook文件中连接时会出错,此时需要将板子断电
scope.dis()
target.dis()

四、补充

如果想直接将保存的数据保存为.trs格式的文件,需要进行以下步骤:

1. 安装trsfile库

在jupyter notebook文件中,执行下面的命令:

%%sh
pip install --upgrade pip	#更新pip
pip install trsfile			#安装trsfile库

当然也可以直接打开ChipWhisperer Bash,然后执行pip install trsfile,执行后记得重启ChipWhisperer。

2. 将数据写入.trs文件的模板函数

import os
import numpy as np
from trsfile import trs_open, Trace, SampleCoding, Header
from trsfile.parametermap import TraceParameterMap, TraceParameterDefinitionMap
from trsfile.traceparameter import ParameterType, TraceParameterDefinition

_type_to_ParameterType={
    'bool':'BOOL',
    'int8':'BYTE','uint8':'BYTE',
    'int16':'SHORT','uint16':'SHORT',
    'int32':'INT','uint32':'INT',
    'int64':'LONG','uint64':'LONG',
    'float16':'FLOAT','float32':'FLOAT',
    'float64':'DOUBLE',
    'str':'STRING','bytes':'STRING'
}
_type_to_SampleCoding = {
        "int8": 'BYTE', "uint8": 'BYTE',
        "int16": 'SHORT', "uint16": 'SHORT',
        "int32": 'INT', "uint32": 'INT',
        "int64": 'INT', "uint64": 'INT',
        "float32": 'FLOAT', "float64": 'FLOAT'
}

def save_trs(header,traces,param:dict, trs_file_path:str):
    header = {Header[key]: value for key, value in header.items()}
    parameter_def,param_offset = {},0
    for key in param:
        param[key]=np.array(param[key],np.uint8)
        param_len=param[key].shape[1]
        param_type=ParameterType[_type_to_ParameterType[param[key].dtype.name]]
        parameter_def[key] = TraceParameterDefinition(param_type, param_len, param_offset)
        param_offset += param_len*param_type.byte_size
    header[Header.LENGTH_DATA] = param_offset
    header[Header.TRACE_PARAMETER_DEFINITIONS] = TraceParameterDefinitionMap(parameter_def)
    if not isinstance(traces,np.ndarray):
        traces=np.array(traces)
    if traces.dtype.name.endswith('64'):
        traces_type_new=traces.dtype.name.replace('64','32')
        traces=np.array(traces,np.dtype(traces_type_new))
    trs_type=SampleCoding[_type_to_SampleCoding[traces.dtype.name]]
    if os.path.exists(trs_file_path):
        os.remove(trs_file_path)
    header[Header.NUMBER_TRACES]=0
    with trs_open(trs_file_path,'w',headers=header,live_update=True) as trs_file:
        for i in range(traces.shape[0]):
            trs_param={}
            for key,value in parameter_def.items():
                trace_parameter=value.param_type.param_class
                trs_param[key]=trace_parameter(param[key][i])
            trs_file.extend(Trace(trs_type,traces[i],TraceParameterMap(trs_param)))

该函数较长,且一般无需更改,可以将其保存为一个文件,本文命名为trsfile_save.py,保存在D:/mycode/chipwhisperer/utils下。

3. 修改jupyter notebook中的保存文件部分

%run D:/mycode/chipwhisperer/utils/trsfile_save.py #通过魔法函数%run执行trsfile_save.py文件

#定义trs文件保存路径
trsfile_path="D:/WMD_DATA/cw305_aes_normal.trs"

#创建trs文件的头,里面可以定义一些有用的信息,具体可通过执行help(Header)查看
header={
	#这里定义了其中的'DESCRIPTION'参数,指明了采集目标,以及主密钥
	'DESCRIPTION':'target: cw305, AES normal; main key:0x0123456789abcdeffedcba9876543210'
	#用户还可以定义其他的一些参数,不过‘NUMBER_TRACES’,'NUMBER_SAMPLES'等参数无需声明,会自动生成
} 

#定义trs文件的param数据,param为一个字典,其中保存和能量迹一一对应的数据,如明文、密文、掩码等
param={
	'plaintext': plaintext,
	'ciphertext': ciphertext
}

#调用save_trs函数进行保存
save_trs(header,traces,param,trsfile_path)

<think>我们正在为嵌入式软件工程师设计一个与通信加密相关的绩效发展计划。根据用户需求,需要包括: 1. 需提升的能力 2. 提升目标 3. 行动计划 4. 完成时间 注意:从嵌入式通信加密角度出发,结合嵌入式系统特点和加密技术应用。 参考思路: 嵌入式通信加密通常涉及: - 加密算法在资源受限设备上的实现(如AES, RSA, ECC, SHA等) - 安全通信协议(如TLS/DTLS, MQTT over TLS, CoAP with DTLS等) - 密钥管理(生成、存储、分发、更新) - 安全启动、固件加密、防篡改等硬件相关安全 - 侧信道攻击防护 因此,我们可以从以下几个方面设计能力发展项: 一、需提升的能力: 1. 加密算法理论与嵌入式实现能力 2. 安全通信协议集成与优化能力 3. 嵌入式系统密钥管理能力 4. 安全认证与防篡改技术 5. 安全漏洞分析与防护能力 二、提升目标: 针对上述能力设定具体可衡量的目标。 、行动计划: 包括学习、实践、项目应用等具体步骤。 四、完成时间: 设定合理的时间节点。 下面我们具体展开:</think>### 嵌入式软件工程师绩效评估 - 通信加密相关能力发展项 结合嵌入式系统资源受限、实时性要求高等特点,通信加密能力的提升需聚焦以下方向: --- #### **1. 需提升的能力** - **加密算法嵌入式优化能力** 需提升在资源受限环境(如低内存MCU)下实现高效加密算法(如AES-CTR、ECC)的能力,避免因加密计算导致系统延迟[^1]。 - **安全协议集成能力** 缺乏在嵌入式通信协议(如MQTT over TLS、CoAP with DTLS)中集成国密算法(SM2/SM4)的实践经验[^2]。 - **侧信道攻击防护能力** 对功耗分析、时序攻击等物理层安全威胁的防护手段掌握不足。 --- #### **2. 提升目标** | 能力方向 | 量化目标 | 完成时间 | |------------------------|--------------------------------------------------------------------------|----------| | 算法优化 | AES-128加解密耗时 ≤ 50μs(STM32F4平台),内存占用 ≤ 2KB | 3个月 | | 国密协议集成 | 在现有嵌入式TCP/IP协议栈中实现SM2密钥交换 + SM4加密通信 | 6个月 | | 侧信道防护 | 通过差分功耗分析(DPA)测试,密钥泄露风险降低90% | 9个月 | --- #### **3. 行动计划** **阶段1:基础强化(1-3个月)** - **学习资源** - 精读《嵌入式系统密码学实践》(Jean-Philippe Aumasson著)第4章 - 完成ARM mbedTLS官方教程的AES/ECC优化实验 - **实践任务** ```c // AES-CTR模式优化示例(减少内存复制) void aes_ctr_encrypt(uint8_t* out, const uint8_t* in, size_t len, const uint8_t* key, uint8_t* ctr) { mbedtls_aes_context ctx; mbedtls_aes_init(&ctx); mbedtls_aes_setkey_enc(&ctx, key, 128); size_t offset = 0; while (len) { uint8_t keystream[16]; mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, ctr, keystream); for (int i=0; i<16 && len; i++, len--, offset++) { out[offset] = in[offset] ^ keystream[i]; // 流式处理减少缓冲 } ctr_increment(ctr); // 自定义轻量级计数器递增 } mbedtls_aes_free(&ctx); } ``` **阶段2:协议集成(4-6个月)** - **项目实战** - 基于LwIP协议栈实现DTLS 1.3 + SM4通信模块 - 通过Wireshark抓包验证握手协议合规性 - **关键指标** $$ \text{握手时间} \leq 500\text{ms} \quad ( \text{RTT} \leq 50\text{ms} ) $$ **阶段3:安全加固(7-9个月)** - **防护措施** - 实现随机时延插入:$$ t_{\text{delay}} = t_{\text{base}} + k \cdot \text{rand()} \quad (k \in [10, 50]\mu s) $$ - 采用掩码技术(Masking)保护AES S-Box查表操作 - **测试验证** 使用ChipWhisperer开发板执行DPA攻击模拟,确保密钥无法被提取[^4]。 --- #### **4. 验证方式** - 算法效率:使用逻辑分析仪测量加解密时钟周期数 - 协议安全:通过国密局GM/T 0024标准认证测试 - 侧信道防护:提交第安全实验室渗透测试报告 --- ### 相关问题 1. **如何平衡嵌入式加密的实时性与安全性?** [提示:动态调整密钥长度$k$的权衡] 2. **国密算法在嵌入式系统中的资源开销如何量化评估?** [提示:对比SM4与AES的时钟周期/内存占用] 3. **TLS握手在低带宽物联网场景下的优化方案有哪些?** [提示:Session Resumption与预共享密钥(PSK)] 4. **侧信道攻击防护是否会显著增加系统功耗?** [提示:掩码技术对功耗的影响模型] > 注:计划需结合具体芯片平台(如ESP32/STM32)调整参数,建议每季度review进展[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秃了头,空悲切

您的支持就是我创作最大的动力~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值