3个版本 (1个稳定版)
1.0.0 | 2020年9月19日 |
---|---|
0.1.1 | 2020年8月27日 |
0.1.0 | 2020年8月27日 |
#744 在 嵌入式开发
每月45次下载
1.5MB
2K SLoC
适用于Microchip加密认证设备(即ATECC608a)的跨平台I2C驱动程序,完全使用Rust编写。此库实现了与Microchip安全设备ATECC608a通信所需的API。
ATECC608A设备是Microchip CryptoAuthentication™系列加密引擎认证设备之一,具有基于硬件的高安全性密钥存储。
ATECC608A设备具有灵活的命令集,适用于许多应用程序,包括以下内容
- 网络/物联网节点保护 - 验证节点ID,确保消息的完整性,并支持密钥协商以创建用于消息加密的会话密钥。
- 反伪造 - 验证可拆卸、可更换或消耗性客户端的真实性。客户端的例子可以是系统配件、电子子板或其他备件。它还可以用于验证软件/固件模块或存储器存储元素。
- 保护固件或媒体 - 在引导时验证存储在闪存中的代码,以防止未经授权的修改,将下载的程序文件加密为常见广播,或唯一加密只能在单个系统上使用的代码镜像。
- 存储安全数据 - 将密钥存储在标准微处理器的加密加速器中。可通过加密/认证的读写操作使用可编程保护。
- 检查用户密码 - 验证用户输入的密码,而不会使预期值变得已知,将可记忆的密码映射到随机数,并与远程系统安全交换密码值。
设备包括一个EEPROM数组,可用于存储多达16个密钥、证书、各种读写操作、只读或秘密数据、消耗日志和安全配置。可以通过多种方式限制对内存各部分的访问,然后锁定配置以防止更改。设备通过标准I2C接口(最高速度为1 Mb/s,见I2C接口部分)进行访问。该接口与标准串行EEPROM I2C接口规范兼容。
ATECC608A是一种基于命令的设备,它从系统中接收命令,执行这些命令,然后返回结果或错误代码。
- 安全命令: 这组命令通常访问EEPROM空间或执行加密计算。
- 加密命令: 这部分安全命令包括所有访问硬件ECC加速器(GenKey、Sign、ECDH、Verify)的ECC命令以及访问硬件SHA加速器(CheckMac、DeriveKey、GenDig、HMAC、MAC、SHA、Nonce)的SHA命令。
功能
- 100%安全的Rust代码,即驱动程序中没有内存安全错误(除非我的逻辑错误)。
- 平台无关,即对所有硬件依赖项使用'embedded-hal'。
- 根本不需要(堆栈)动态内存分配。使用
heapless
和Postcard
进行命令数据包构造。 - 与Microchip的CrypoAuthlib 'C库'的API兼容,即使用相同的名称和参数,使其更容易绑定到现有的C代码库。
注意事项
- 这个驱动程序是我对“学习语言”感兴趣的结果。所以,它并不完美(或生产质量),可能还有更好的做事方式。欢迎反馈、评论和建议。
- 使用'Postcard'将Rust结构体序列化或反序列化为'heapless Vec'(即无_std环境的栈向量)。
- 此代码已在nrf板使用'nrf-hal-common'进行测试。理论上,它应该与实现'embedded-hal'特质的任何HAL一起工作。
- 在开发过程中,我在nrf52840的i2c/TWIM实现(在nrf52840_hal中)中发现了错误。
- 已提交PR并合并了修复,但请确保拉取修复或相应地编辑cargo.toml,以便使此驱动程序正常工作。https://github.com/nrf-rs/nrf-hal/pull/166
- 构建此驱动程序rust版本的参考。
- CryptoAuthLib的Libc版本 - Microchip的加密认证库 (https://github.com/MicrochipTech/cryptoauthlib)
- cryptoauthlib的Micropython端口 - https://github.com/dmazzella/ucryptoauthlib
- ATECC508A数据表 - https://bit.ly/2YzqC00
用法
#![no_main]
#![no_std]
// #![allow(warnings)]
extern crate nrf52840_hal as hal;
extern crate panic_halt;
extern crate nrf52840_mdk;
use cortex_m_rt::{entry, exception};
use hal::gpio::{p0, p1};
use hal::target::Peripherals;
use hal::timer::Timer;
use hal::twim::{self, Twim};
// use cortex_m_semihosting::hprintln;
use Rusty_CryptoAuthLib::ATECC608A;
use nrf52840_mdk::Pins;
#[entry]
fn main() -> ! {
let p = Peripherals::take().unwrap();
let pins = Pins::new(p0::Parts::new(p.P0), p1::Parts::new(p.P1));
let scl = pins.p27.into_floating_input().degrade();
let sda = pins.p26.into_floating_input().degrade();
let i2c_pins = twim::Pins { scl, sda };
let i2c = Twim::new(p.TWIM1, i2c_pins, twim::Frequency::K100);
let delay = Timer::new(p.TIMER0);
let timer = Timer::new(p.TIMER1);
let mut atecc608a = ATECC608A::new(i2c, delay, timer).unwrap();
// GENKEY COMMAND EXAMPLE
// Note: TFLXTLSConfig has slot 2 configured to hold an ECC private key.
// So, only GENKEY AND PRIVWRITE commands can be used to write (i.e. store or generate private keys) to this slot.
// Check `Slot access policies` section in my GitHub readme for more info.
let slot = 0x02;
let gen_public_key = match atecc608a.atcab_genkey(slot) { // public key retreived upon
Ok(v) => v, // generating and storing a new (random) ECC private key
Err(e) => panic!("Error generating ECC private key: {:?}", e), // in slot 2.
};
let comp_public_key = match atecc608a.atcab_get_pubkey(slot) { // public key computed from
Ok(v) => v, // the previously generated and stored
Err(e) => panic!("Error retrieving ECC public key: {:?}", e), // private key in slot 2.
};
assert_eq!(&gen_public_key[..], &comp_public_key[..]);
loop {}
}
#[exception]
fn HardFault(ef: &cortex_m_rt::ExceptionFrame) -> ! {
panic!("HardFault at {:#?}", ef);
}
#[exception]
fn DefaultHandler(irqn: i16) {
panic!("Unhandled exception (IRQn = {})", irqn);
}
请参阅示例文件夹以获取更多信息。
设备个性化(即配置)步骤
ATECC608A是一种基于命令的设备,它接收来自系统的命令,执行这些命令,然后返回一个结果或错误代码。它包含一个集成的EEPROM存储内存和SRAM缓冲区。EEPROM内存总共包含1400字节,分为以下区域
- 配置区域:128字节包含设备配置信息,例如每个槽位的访问策略、序列号、锁定信息等。
- 数据区域:1208字节(分为16个通用只读或读写内存槽)。
- OTP区域:64字节
在我们开始之前,我们需要使用将确定每个数据槽响应的访问策略的值来编程Config区域。配置区域可以修改,直到它被锁定(LockConfig设置为不为0x55)。为了启用访问策略,必须设置LockValue字节。以下是出厂配置与示例(ATECC-TFLXTLS)配置的比较。(示例配置来自Microchip。它用于其一些预配置变体。)
将ATECC-TFLXTLS配置写入设备将产生一个个性化设备,即您现在可以生成/存储密钥、证书和其他内容在设备的EEPROM槽中,如下表所示。有关每个槽位访问策略和可以在此槽上使用的命令的更详细视图,请参阅详细的槽位访问策略
槽位 | 用例 |
---|---|
0 | 主私钥 |
1 | 内部签名私钥 |
2 | 次要私钥1 |
3 | 次要私钥2 |
4 | 次要私钥3 |
5 | 秘密密钥 |
6 | IO保护密钥 |
7 | 安全启动摘要 |
8 | 通用数据 |
9 | AES密钥 |
10 | 设备压缩证书 |
11 | 签名者公钥 |
12 | 签名者压缩证书 |
13 | 父公共密钥或通用数据 |
14 | 验证过的公共密钥 |
15 | 安全启动公共密钥 |
重要:在继续之前,请阅读本节
- 要将ATECC-TFLXTLS配置字节写入设备,请使用示例文件
atcab_write_config_zone.rs
,该文件包含在示例文件夹中。 - 使用读取命令示例 -
atcab_read_config_zone.rs
读取配置区域的内容(即所有128个字节)。这只是双重检查,以确保正确的字节集(即ATECC-TFLXTLS配置字节)已写入设备。 - 在我们开始填充(即从或写入)数据或OTP区域之前,我们必须锁定配置区域。使用
atcab_lock_config_zone_crc.rs
示例锁定配置区域。注意:锁定配置区域是一个不可逆的操作。因此,请在进行之前确保一切正确。 - 完成此操作后,您可以开始使用以下表格中所示的选择将私有的ECC密钥或外部生成的公钥填充设备的数据区域。
- 示例
atcab_sign_and_verify.rs
正是如此。它生成并加载一个新(随机)的ECC私钥到设备的3号槽位。私钥在设备内部生成且从未离开。示例包含一个测试消息,该消息由私钥签名并由相应的“计算”公钥验证。 - 注意:在第3步中,您可以为所有私钥槽位(0-4)执行此操作,因为并非所有访问策略都严格实施。例如,槽位0-1中的私钥是永久的,而槽位2-4是可更新的,根据策略。但是,因为数据区域未锁定,所以像
WRITE或GENKEY
这样的命令仍然可以在没有限制的情况下工作,即使槽位配置为(永久)不可写入。 - 换句话说,这是您实际上将所有槽位填充到设备中的密钥、证书和其他数据的地方,当您对数据区域的内容满意时,请转到第4步。
- 示例
- 只有锁定数据区域(即配置区域的字节[86]或
LockValue
)后,槽位的访问策略才会严格实施。例如:您不能再为0号和1号槽位发出GENKEY命令来生成新的随机私钥,但您可以为0号和1号槽位中对应的永久私钥发出GENKEY来计算其公钥。- 注意:我尚未实现锁定数据区域的方法。我将添加更多需要测试的命令。如果您需要它,请留言。
目前支持的命令是
- INFO
- LOCK
- READ (1)
- SHA (1)
- WRITE (1) -
- VERIFY (1)
- GENKEY
- SIGN
- NONCE
将添加对以下命令的支持
- SELFTEST
- RANDOM
- SECUREBOOT
- AES
- KDF
(1) 并非所有功能都已实现,请参见以下列表以获取详细信息
目前实现的方法是
- atcab_version()
- atcab_get_addr(zone, slot=0, block=0, offset=0)
- atcab_get_zone_size(zone, slot=0)
- atcab_checkmac(mode, key_id, challenge, response, other_data)
- atcab_counter(mode, counter_id)
- atcab_counter_increment(counter_id)
- atcab_counter_read(counter_id)
- atcab_derivekey(mode, key_id, mac)
- atcab_ecdh_base(mode, key_id, public_key)
- atcab_ecdh(key_id, public_key)
- atcab_ecdh_enc(key_id, public_key, read_key, read_key_id)
- atcab_ecdh_ioenc(key_id, public_key, io_key)
- atcab_ecdh_tempkey(public_key)
- atcab_ecdh_tempkey_ioenc(public_key, io_key)
- atcab_gendig(zone, key_id, other_data)
- atcab_genkey_base(mode, key_id, other_data=None)
- atcab_genkey(key_id)
- atcab_get_pubkey(key_id)
- atcab_hmac(mode, key_id)
- atcab_info_base(mode=0)
- atcab_info()
- atcab_kdf(mode, key_id, details, message)
- atcab_lock(mode, crc=0)
- atcab_lock_config_zone()
- atcab_lock_config_zone_crc(crc)
- atcab_lock_data_zone()
- atcab_lock_data_zone_crc(crc)
- atcab_lock_data_slot(slot)
- atcab_mac(mode, key_id, challenge)
- atcab_nonce_base(mode, zero=0, numbers=None)
- atcab_nonce(numbers=None)
- atcab_nonce_load(target, numbers=None)
- atcab_nonce_rand(numbers=None)
- atcab_challenge(numbers=None)
- atcab_challenge_seed_update(numbers=None)
- atcab_priv_write(key_id, priv_key, write_key_id, write_key)
- atcab_random()
- atcab_read_zone(zone, slot=0, block=0, offset=0, length=0)
- atcab_read_serial_number()
- atcab_read_bytes_zone(zone, slot=0, block=0, offset=0, length=0)
- atcab_is_slot_locked(slot)
- atcab_is_locked(zone)
- atcab_read_config_zone()
- atcab_read_enc(key_id, block, data, enc_key, enc_key_id)
- atcab_cmp_config_zone(config_data)
- atcab_read_sig(slot)
- atcab_read_pubkey(slot)
- atcab_secureboot(mode, param2, digest, signature)
- atcab_secureboot_mac(mode, digest, signature, num_in, io_key)
- atcab_selftest(mode, param2=0)
- atcab_sha_base(mode=0, data=b'', key_slot=None)
- atcab_sha(data)
- atcab_sha_hmac(data, key_slot, target)
- atcab_sign_base(mode, key_id)
- atcab_sign(key_id, message)
- atcab_sign_internal(key_id, is_invalidate=False, is_full_sn=False)
- atcab_updateextra(mode, value)
- atcab_verify(mode, key_id, signature, public_key=None, other_data=None, mac=None)
- atcab_verify_extern(message, signature, public_key)
- atcab_verify_extern_mac(message, signature, public_key, num_in, io_key, is_verified)
- atcab_verify_stored(message, signature, key_id)
- atcab_verify_stored_mac(message, signature, key_id, num_in, io_key, is_verified)
- atcab_verify_validate(key_id, signature, other_data, is_verified)
- atcab_verify_invalidate(key_id, signature, other_data, is_verified)
- atcab_write(zone, address, value=None, mac=None)
- atcab_write_zone(zone, slot=0, block=0, offset=0, data=None)
- atcab_write_bytes_zone(zone, slot=0, offset=0, data=None)
- atcab_write_pubkey(slot, public_key)
- atcab_write_config_zone(config_data)
- atcab_write_enc(key_id, block, data, enc_key, enc_key_id)
- atcab_write_config_counter(counter_id, counter_value)
支持
对于问题、问题、功能请求和其他更改,请在github项目中进行问题报告。
许可证
根据您的选择,许可为以下之一
- Apache许可证,版本2.0(LICENSE-APACHE或http://www.apache.org/licenses/LICENSE-2.0)
- MIT许可证(LICENSE-MIT或http://opensource.org/licenses/MIT)
。
贡献
除非您明确表示,否则任何旨在包含在您的工作中的贡献,如Apache-2.0许可证中定义,均应双重许可,如上所述,无需任何附加条款或条件。