0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

【微五科技CF3310开发板试用体验】利用硬件加密功能,搞定串口加密通讯

开发板试用精选 来源:开发板试用 作者:电子发烧友论坛 2022-11-23 14:20 1144次阅读

本文来源电子发烧友社区,作者:HonestQiao, 帖子地址:https://bbs.elecfans.com/jishu_2289947_1_1.html

CF3310开发板是国产首创RISC-V安全微控制器,从官方资料可以了解到其所支持的安全功能:

image.png

其在硬件上,对加密功能进行了针对性的加强:

image.png

并在硬件级别提供了多种加密算法

image.png

在经过学习官方资料包中提供的demo,以及经过官方技术人员手把手的指导,结合自己所了解的Python加密知识,实现了串口加密通信

这篇分享中,演示的是从开发板发送经过加密处理的数据;

然后在上位机上,接收并解密对应的数据。

在开始之前,请先阅读我的上一篇分享【CF3310开发板的串口使用】 ,了解串口的使用。

可以直接使用跳线帽,按照如图所示短接对应的引脚,这样Type-C口连接到电脑后,就能直接使用串口工具连接其对应的串口进行调试了:

image.png

然后,我选择的加密方式为AES_CBC:

image.png

先查看官方演示代码的src/demo/algo_demo.c中AES部分的代码,来了解加密解密的具体处理。

因为要使用AES_CBC加密,所以重点关注其中:aes.type = ALG_ECBaes.KBits = ALG_KEY128部分的代码:

aes.mode = ALG_ENCRYPT;
	aes.type = ALG_ECB;

	aes.KBits = ALG_KEY128;

	HAL_AES_SetSecurityLevel(&aes);

	if(HAL_AES_Cryptographic(&aes,(uint8_t *)key,(uint8_t *)iv,(uint8_t *)plain,temp,32))
	{
		RETERR();
	}

	if(memcmp((uint8_t *)cipher_aes128_ecb,temp,32) != 0)
	{
		RETERR();
	}

	printf("	通过
");

上述代码中,重点是加密函数:

uint8_t HAL_AES_Cryptographic(AES_ConfigTypeDef *hcfg,uint8_t *pKey,uint8_t *pIV,uint8_t *srcBuffer,uint8_t *destBuffer,uint32_t length)

官方提供的各种加密算法,都通过该函数进行调用,来获取加密后的结果。

其参数分别为:

  • hcfg:AES加密定义
  • pKey:加密密钥
  • pIV:加密偏移向量
  • srcBuffer:明文
  • destBuffer:加密结果
  • length:块大小

最终,实际实现的代码如下:

/**
 * [url=home.php?mod=space&uid=1455510]@file[/url]    uart_sec.c
 * [url=home.php?mod=space&uid=40524]@author[/url]  HonestQiao
 * [url=home.php?mod=space&uid=644434]@version[/url] V1.0
 * @date    2022.6.28
 * [url=home.php?mod=space&uid=2666770]@Brief[/url]   uart sec test
 *
 */
// eport
#include "eport_hal.h"

// uart
#include "uart_hal.h"

// algo
#include "algo_demo.h"
#include "algo_hal.h"
#include "trng_hal.h"

#include "cpm_hal.h"
#include "hal.h"

#include "delay.h"
#include "debug.h"

uint8_t uart_tx_buf[128];
uint8_t uart_rx_buf[128];
UART_HandleTypeDef UART_Handle;

int32_t counter = 0;

void AES_SEND_TEST(void)
{
    AES_ConfigTypeDef aes;

    uint8_t len = 0;
    uint8_t loop = 0;

    uint8_t key[32] = "1234567890654321";   // 加加密密钥
    uint8_t iv[] = "9876543210123456";      // AES_ECB实际不用
    uint8_t plain[32];                      // 非存放明文
    uint8_t temp[32];                       // 临时存放加密后的结果

    len = strlen(key);
    memset(key + len, 0x00, 32 - len);      // 将加密密钥用 0x00 填充到32位
    memset(plain, 0x00, 32);                // 明文清空
    memset(temp, 0x00, 32);                 // 加密结果存放清空

    counter++;                              // 计数器递增
    if (counter > 999999)                   // 最大6位数
    {
        counter = 1;
    }
    sprintf(plain, "%06d", counter);        // 将要被加密的明文信息

    // printf("key=%s len=%d
", key, sizeof(key) / sizeof(key[0]));
    // printf("plain=%s len=%d
", plain, sizeof(plain) / sizeof(plain[0]));

    // 设置AES_ECB128加密
    aes.level = dpa_disable;
    aes.type = ALG_ECB;
    aes.KBits = ALG_KEY128;
    aes.mode = ALG_ENCRYPT;
    HAL_AES_SetSecurityLevel(&aes);

    // 加密处理
    if (HAL_AES_Cryptographic(&aes, (uint8_t *)key, (uint8_t *)iv, (uint8_t *)plain, temp, 32))
    {
        memset(uart_tx_buf, 0x00, 256);
        sprintf(uart_tx_buf, "ERROR:AES_256_ECB crypt error.
");
        HAL_UART_Transmit(&UART_Handle,
                          uart_tx_buf,
                          sizeof(uart_tx_buf) / sizeof(uart_tx_buf[0]),
                          1000000);
        return;
    }

    // 将加密结果转换为需要发送的ASCII字符串
    memset(uart_tx_buf, 0x00, 256);
    sprintf(uart_tx_buf, "DATA:%06d:", counter);    // 测试阶段,将明文也发送过去
    len = strlen(uart_tx_buf);
    for (loop = 0; loop < 32; loop++)
    {
        uart_tx_buf[len + loop * 3 + 0] = "0123456789ABCDEF"[(temp[loop] >> 4) & 0x0F];     // 高位转换为ASCII字符
        uart_tx_buf[len + loop * 3 + 1] = "0123456789ABCDEF"[temp[loop] & 0x0F];            // 低位转换为ASCII字符
        uart_tx_buf[len + loop * 3 + 2] = ' ';                                              // 空格
    }
    uart_tx_buf[len + loop * 3] = '
';                                                     // 结尾

    len = len + loop * 3 + 1;                                                               // 计算发送长度

    // 通过串口发送数据
    HAL_UART_Transmit(&UART_Handle,
                      uart_tx_buf,
                      len,
                      1000000);
}

// 串口初始化
void Bsp_UARTInit1(UART_HandleTypeDef *huart, UART_TypeDef *puart)
{
    huart->Init.BaudRate = 115200;
    huart->Init.IPSFreq = g_ips_clk;
    huart->Init.Parity = UART_PARITY_MODE_NONE;
    huart->Init.WordLength = UART_WORDLENGTH_8B;
    huart->Instance = puart;

    HAL_UART_Init(huart);
}

// 实际测试主体逻辑
void UART_CPU_SEC_RxTxTest(void)
{
    // LED引脚设置
    EPORT_InitTypeDef eport;
    EPORT_PinDef pin_num;
    EPORT_TypeDef *eport_base;

    pin_num = EPORT_PIN4;
    eport_base = EPORT;

    eport.pin = pin_num;
    eport.func = GPIO_FUN;
    eport.dir = GPIO_DIR_OUT;
    eport.output_mode = EPORT_OUTPUT_MODE_CMOS;
    eport.pull_mode = EPORT_PULL_UP;
    HAL_EPORT_Init(eport_base, &eport);

    // algo设置
    TRNG_HandleTypeDef htrng;
    htrng.instance = TRNG;
    htrng.dividor = 59;
    HAL_CPM_ModuleClkCmd(MODULE_CLK_CRYPTO, ENABLE);
    HAL_TRNG_Init(&htrng);

    int8_t status = 0;
    uint16_t loop = 0;

    HAL_StatusTypeDef tmp_status;

    /* init */
    Bsp_UARTInit1((UART_HandleTypeDef *)(&UART_Handle), UART);

    while (1)
    {
        HAL_UART_DisItTcie(&UART_Handle);
        HAL_UART_DisItTie(&UART_Handle);
        HAL_UART_DisItRe(&UART_Handle);

        while (1)
        {
            // 让LED交替闪亮,以示程序正在运行
            HAL_EPORT_TogglePin(eport_base, pin_num);
            if (!status)
            {
                HAL_EPORT_WritePin(eport_base, pin_num, BIT_RESET);
            }
            else
            {
                HAL_EPORT_WritePin(eport_base, pin_num, BIT_SET);
            }
            status = !status;

            // AES串口加密数据发送测试
            AES_SEND_TEST();

            DelayMS(1000);
        }
    }
}

// 测试入口函数
void UART_SEC_Run(void)
{
    UART_CPU_SEC_RxTxTest();
}

将上述代码覆盖掉src/demo/uart_demo.c,然后在src/inc/demo.h中开启UART,就能编译下载了。

以上的代码,整合了EPORT控制PIN4,UART数据发送,以及ALGO加密算法,每1秒钟发送一次数据。

所以实际的连线如下:

iShot_2022-06-24_17.10.36.png

代码中,使用的:

  • 加密算法为:AES_ECB_128
  • 加密密钥为:1234567890654321
  • 加密的明文为:counter计数器,每秒递增1次,然后前面补0到6位长度,如:0000109
  • 串口发送的实际数据:DATA:6位明文:密文HEX的ASCII码

发送的数据中,带有明文的目的,是为了方便上位机测试验证。

将开发板使用Type-C连接到电脑,在上位机中,使用串口工具监听串口,然后编译下载运行以上代码,就可以收到如下串口信息了:

image.png

DATA:6位明文:后面的部分,就是6为明文加密后对应的HEX值,我们获取该值,然后使用AES_ECB进行解密,就能还原得到明文了。

解密的部分,我使用Python来实现的。

首先,需要安装pycrypt:

  • Linux/macOS:pip3 install pycryptodome
  • Windows:pip install pycrypto

然后,使用如下的代码:

import serial
import serial.tools.list_ports
import hashlib
import base64
from Crypto.Cipher import AES
from Crypto.Cipher import DES
from Crypto.Util.Padding import pad, unpad

password = b''

class Communication():

    #初始化
    def __init__(self,com,bps,timeout):
        self.port = com
        self.bps = bps
        self.timeout =timeout
        global Ret
        try:
            # 打开串口,并得到串口对象
             self.main_engine= serial.Serial(self.port,self.bps,timeout=self.timeout)
            # 判断是否打开成功
             if (self.main_engine.is_open):
               Ret = True
        except Exception as e:
            print("---异常---:", e)

    #打开串口
    def Open_Engine(self):
        self.main_engine.open()

    #关闭串口
    def Close_Engine(self):
        self.main_engine.close()
        print(self.main_engine.is_open)  # 检验串口是否打开

    # 打印可用串口列表
    @staticmethod
    def Print_Used_Com():
        port_list = list(serial.tools.list_ports.comports())
        print(port_list)

    def Read_Size(self,size):
        return self.main_engine.read(size=size)

    #接收一行数据
    # 使用readline()时应该注意:打开串口时应该指定超时,否则如果串口没有收到新行,则会一直等待。
    # 如果没有超时,readline会报异常。
    def Read_Line(self):
        return self.main_engine.readline()

    #发数据
    def Send_data(self,data):
        self.main_engine.write(data)

    def Recive_data(self,way):
        # 循环接收数据,此为死循环,可用线程实现
        print("开始接收数据:")
        data = b''
        while True:
            try:
                # 一个字节一个字节的接收
                if self.main_engine.in_waiting:
                    if(way == 0):
                        for i in range(self.main_engine.in_waiting):
                            print("接收ascii数据:"+str(self.Read_Size(1)))
                            data1 = self.Read_Size(1).hex()#转为十六进制
                            data2 = int(data1,16)#转为十进制
                            print("收到数据十六进制:"+data1+"  收到数据十进制:"+str(data2))
                    if(way == 1):
                        #整体接收
                        # data = self.main_engine.read(
                        #		self.main_engine.in_waiting).decode("utf-8")#方式一
                        tmp = self.main_engine.read_all()#方式二
                        print("接收ascii数据:", tmp)                        
                        data = data + tmp
                        if tmp[-1] == 10:
                            data_hex = bytes.fromhex(data[12:-2].decode())
                            data_plain = data[5:11].decode()
                            print("	接收到一批数据:", data[12:-2])                            
                            print("	接收到的明文数据:", data_plain)                            
                            date_de = decrypt(data_hex, password)
                            data_de_plain = date_de.decode()
                            print("	解码后的明文数据:", data_de_plain)
                            data = b''                            
                        
            except Exception as e:
                print("异常报错:",e)

def encrypt(data, password):
    bs = AES.block_size
    pad = lambda s: s.ljust(bs,b'') 
    cipher = AES.new(password, AES.MODE_ECB)
    data_used = pad(data)
    print("pad(data):", len(data_used), data_used)
    data = cipher.encrypt(data_used)
    return (data)

def decrypt(data, password):
    bs = AES.block_size
    if len(data) < bs:
        print("direct return",data,len(data),bs)
        return (data)
    cipher = AES.new(password, AES.MODE_ECB)
    data  = cipher.decrypt(data)
    return (data)

if __name__=="__main__":
    AES.block_size = 32
    password = b'1234567890654321' #16,24,32位长的密码

    com = Communication("/dev/cu.usbserial-14240", 115200, None)
    com.Print_Name()
    plain,denc_hex = com.Recive_data(1)

将以上代码,保存为:uart_sec_recv.py,准备执行。执行以前,要先关掉串口监听工具。

使用python uart_sec_recv.py执行,开始监听串口,就能接收到串口数据,并进行解码:

image.png

从上述结果中可以看到,解码后的数据,与传送过来的明文数据,是一致的,因此加密发送并解码成功。

以上的分享,演示的是开发板发送加密数据,使用了AES_ECB128加密算法,参考algo_demo.c,可以试用其他的加密算法,当然,上位机的代码也需要进行针对性的处理。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 微五科技
    +关注

    关注

    1

    文章

    27

    浏览量

    2654
  • 开发板试用
    +关注

    关注

    3

    文章

    301

    浏览量

    2225
  • CF3310
    +关注

    关注

    0

    文章

    23

    浏览量

    373
收藏 人收藏

    相关推荐

    免费丨米尔 STM32MP257开发板有奖试用

    米尔与ST合作发布的新品基于STM32MP257应用处理器的MYD-LD25X开发板免费试用活动来啦~~米尔提供了2块价值488元的MYD-LD25X开发板发起试用活动您不仅可以免费
    的头像 发表于 03-20 08:05 122次阅读
    免费丨米尔 STM32MP257<b class='flag-5'>开发板</b>有奖<b class='flag-5'>试用</b>

    有奖丨米尔 瑞芯YR3562开发板免费试用

    米尔与瑞芯合作发布的新品基于瑞芯RK3562应用处理器的MYD-YR3562开发板免费试用活动来啦~~米尔提供了3块价值599元的MYD-YR3562
    的头像 发表于 03-20 08:05 192次阅读
    有奖丨米尔 瑞芯<b class='flag-5'>微</b>YR3562<b class='flag-5'>开发板</b>免费<b class='flag-5'>试用</b>

    有奖试用!!RA-Eco-RA4M2-100PIN-V1.0开发板试用活动报名

    RA-Eco-RA4M2-100PIN-V1.0开发板试用活动
    的头像 发表于 03-13 12:10 168次阅读
    有奖<b class='flag-5'>试用</b>!!RA-Eco-RA4M2-100PIN-V1.0<b class='flag-5'>开发板</b><b class='flag-5'>试用</b>活动报名

    STM32/GD32开发板基本串口RS232\\RS485通信及CAN接口 usbhid升级 U盘升级 sd卡升级升级学习方案开发板

    使用VS2022开发平台,C#语言开发,该开发板提供上位机工程源码。 bin文件AES256加密解密升级功能!有以下例程:1、CAN总线升级
    发表于 02-27 14:56

    支持远程脱机密文下载加密芯片SMEC系列编程器开发板介绍

    这里主要介绍下SMEC80ST、SMEC88SP/ST、SMEC98SP编程器开发板(后文简称SMEC编程器)的远程脱机密文下载功能。芯片资料可在http://www.sinormous.com
    发表于 02-14 16:19

    瑞芯开发板/主板Android调试串口配置为普通串口方法

    ​本文介绍瑞芯开发板/主板Android调试串口配置为普通串口方法,不同型找到对应文件修改,修改的方法相通。触觉智能RK3562
    的头像 发表于 02-11 11:57 375次阅读
    瑞芯<b class='flag-5'>微</b><b class='flag-5'>开发板</b>/主板Android调试<b class='flag-5'>串口</b>配置为普通<b class='flag-5'>串口</b>方法

    支持远程脱机密文下载加密芯片SMEC编程器开发板介绍

    这里主要介绍下SMEC80ST、SMEC88SP/ST、SMEC98SP编程器开发板(后文简称SMEC编程器)的远程脱机密文下载功能。SMEC编程器主芯片本身是一个颗具有最高安全等级的银行
    发表于 01-08 11:30

    STM32配合可编程加密芯片SMEC88ST的防抄加密方案设计

    芯片部分核心的算法、功能代码以及核心数据放入加密芯片,开发者设计的加密方案更加灵活、安全、可靠。 本方案选用具有智能卡32位内核的加密芯片S
    发表于 12-27 13:03

    瑞芯平台Android系统串口测试方法,触觉智能RK3562开发板演示

    瑞芯方案主板Android系统串口测试方法,通用RK3568、RK3566、RK3588、RK3576等。触觉智能RK3562开发板演示
    的头像 发表于 12-24 11:51 561次阅读
    瑞芯<b class='flag-5'>微</b>平台Android系统<b class='flag-5'>串口</b>测试方法,触觉智能RK3562<b class='flag-5'>开发板</b>演示

    浅谈加密芯片的一种破解方法和对应加密方案改进设计

    目前市面上很多防抄加密方案都是基于加密芯片的安全存储和密文通讯来实现对主MCU方案的保护。比如把主MCU用到的一些参数、配置信息等存储在加密
    发表于 12-20 15:31

    追加名额丨米尔瑞芯RK3576开发板有奖试用

    米尔与瑞芯合作发布的新品基于瑞芯RK3576应用处理器的MYD-LR3576开发板免费试用活动加码啦~~米尔追加了2块价值849元的MYD-LR3576
    的头像 发表于 11-22 01:00 411次阅读
    追加名额丨米尔瑞芯<b class='flag-5'>微</b>RK3576<b class='flag-5'>开发板</b>有奖<b class='flag-5'>试用</b>

    有奖丨米尔 瑞芯RK3576开发板免费试用

    米尔与瑞芯合作发布的新品基于瑞芯RK3576应用处理器的MYD-LR3576开发板免费试用活动来啦~~米尔提供了7块价值849元的MYD-LR3576
    的头像 发表于 11-12 01:00 522次阅读
    有奖丨米尔 瑞芯<b class='flag-5'>微</b>RK3576<b class='flag-5'>开发板</b>免费<b class='flag-5'>试用</b>

    有奖试用!!RA-Eco-RA4E2-64PIN-V1.0开发板试用活动开始报名

    有奖试用!!RA-Eco-RA4E2-64PIN-V1.0开发板试用活动开始报名
    的头像 发表于 11-09 01:02 415次阅读
    有奖<b class='flag-5'>试用</b>!!RA-Eco-RA4E2-64PIN-V1.0<b class='flag-5'>开发板</b><b class='flag-5'>试用</b>活动开始报名

    安卓APP开发中,如何使用加密芯片?

    加密芯片是一种专门设计用于保护信息安全的硬件设备,它通过内置的加密算法对数据进行加密和解密,以防止敏感数据被窃取或篡改。如下图HD-RK3568-IOT工控
    的头像 发表于 10-31 17:43 670次阅读
    安卓APP<b class='flag-5'>开发</b>中,如何使用<b class='flag-5'>加密</b>芯片?

    esp32加密后无法关闭怎么解决?

    我使用esp32_devkitc_v4开发板,这测试flash加密功能 ,但当我根据文档所示https://docs.espressif.com/projects/esp
    发表于 06-06 06:48