DS28E01典型应用及破解方法



DS28E01一般应用在加密保护,防止产品被轻易copy盗版。


简单介绍DS28E01:

DS28E01通过1条单总线与MCU通信,单总线不多说了,要求时间非常严格,精确到us级别。

DS28E01有四个存储区:

数据存储器(EEPROM)(共分4页,每页32个字节)

密钥存储器(secret)(8字节)

含有特定功能和用户字节的寄存器页(register page

易失性暂存器(scratchpad)(8字节)


MCU通过单总线只能读写暂存器,而不能直接读写其它存储区。

向数据存储器写数据、载入初始密钥或者向寄存器页写数据时,先把数据写入暂存器,然后通过相应的命令,让芯片自行从暂存器拷贝数据至目的地址。


工作原理:

芯片内部有一个SHA-160加密模块,参与SHA算法的为55字节特定格式的数据,

这些数据包含8字节密钥,5字节用户指定的随机数,32字节EEPROM内容,7字节ROMID,2字节固定数据(0xFF)和1字节EEPROM的地址TA1。

MCU可以读取芯片通过SHA加密后的20字节哈希值,并与MCU自己通过同样的算法计算出的哈希值进行比对。

既然MCU要进行同样的加密操作,要么肯定要生成与芯片内部完全相同的55字节消息,怎么得来的呢?

8字节密钥是自己生成并写进去的.               ->OK

5字节随机数是在芯片执行SHA之前写进暂存器的值.   ->OK

32字节EEPROM数据,在读回20字节哈希值之前,芯片会传回这32字节内容。 ->OK

7字节ROMID,可以在任何时候读到芯片的ROMID.   ->OK

2字节固定值,看手册可以知道   ->OK

1字节TA1,自己写进去的.   ->OK


典型应用过程:

过程一:初始化DS28E01密钥

初始化密钥只在产品出产前在工厂进行操作,只需要操作一次即可。

程序流程:

1.读取芯片ROMID

2.通过一定的算法生成一个唯一的64位的密钥,保证每块主板产生的密钥各不相同。

3.将密钥写入芯片暂存区,并读回验证写入是否正确

4.执行芯片加载密钥命令,让芯片将暂存区中的64位密钥保存至密钥存储区

5.完成。


过程二:验证DS28E01密钥

验证密钥是在产品应用程序中进行,每次启动产品时都会验证DS28E01密钥是否正确,

验证通过则正常运行,验证不正确则通过一定手段让产品工作不正常。

程序流程:

1.读取芯片ROMID

2.通过与初始化过程中相同的算法,生成64位密钥

3.向芯片暂存区写入8字节随机数(只用到其中5个字节),并读回验证

4.向芯片发加密认证命令,可以读回32字节EEPROM数据和20字节哈希值

5.用上面读到数据,生成55字节摘要消息,并进行SHA1运算

6.比较自己计算出的哈希值和从芯片读回的哈希值是否一致


破解方法:

从上面的应用过程可以看出,这里的关键算法是SHA1,而参与SHA计算的数据有两份,一份在芯片内部,我们是无法读出的,

但是另一份却是在MCU内部生成的,所以只要获取到MCU内部生成消息的过程就完成了破解。

而其中关键数据是8字节的密钥,因为这8字节密钥是一般情况是绑定的ROMID和CPUID的,

因此只要能从程序中分析出密钥生成算法就可以达到破解的目的,这个过程只是时间问题。


结论:

加密芯片并没有什么卵用,再复杂的加密算法也只是增加了破解的难度,并不能从根本上解决问题。

防止产品被破解和盗版的唯一方法只有一个:那就是防止从产品中读取到正确二进制代码。

可惜目前还没有什么芯片的FLASH内容是不能读出来。


加密与解密就像矛与盾,是一对永恒的对立面,此增彼长,无穷无尽。


  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
以下是一份基于C语言的DS28E01示例代码,你可以根据自己的需求进行修改: ``` #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdbool.h> #include <string.h> #include "ds28e01.h" // 定义DS28E01的ROM ID const uint8_t ROM_ID[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }; // 初始化1-Wire总线 void ds28e01_init(void) { // TODO: 初始化1-Wire总线 } // 读取DS28E01的ROM ID bool ds28e01_read_rom_id(uint8_t* rom_id) { bool success = true; // 发送读取ROM ID的命令 ds28e01_write_byte(0x33); // 读取ROM ID for (int i = 0; i < 8; i++) { rom_id[i] = ds28e01_read_byte(); if (rom_id[i] != ROM_ID[i]) { success = false; break; } } return success; } // 读取DS28E01中指定地址的数据 bool ds28e01_read_data(uint16_t addr, uint8_t* data, uint16_t len) { bool success = true; // 发送读取数据的命令 ds28e01_write_byte(0xF0); ds28e01_write_byte((uint8_t)(addr >> 8)); ds28e01_write_byte((uint8_t)addr); // 读取数据 for (int i = 0; i < len; i++) { data[i] = ds28e01_read_byte(); } return success; } // 写入DS28E01中指定地址的数据 bool ds28e01_write_data(uint16_t addr, uint8_t* data, uint16_t len) { bool success = true; // 发送写入数据的命令 ds28e01_write_byte(0x0F); ds28e01_write_byte((uint8_t)(addr >> 8)); ds28e01_write_byte((uint8_t)addr); // 写入数据 for (int i = 0; i < len; i++) { ds28e01_write_byte(data[i]); } return success; } // 写入一个字节到1-Wire总线 void ds28e01_write_byte(uint8_t byte) { // TODO: 写入一个字节到1-Wire总线 } // 从1-Wire总线读取一个字节 uint8_t ds28e01_read_byte(void) { // TODO: 从1-Wire总线读取一个字节 return 0; } ``` 注意,上述代码中的`ds28e01_init`、`ds28e01_write_byte`和`ds28e01_read_byte`函数需要根据实际情况进行实现。此外,为了保证数据的安全性,建议在实际使用中加入固件挑战-响应协议。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值