#安全 #I2C驱动器 #认证 #密钥存储 #加密 #atecc608a #微芯驱动器

无std Rusty_CryptoAuthLib

适用于Microchip加密认证硬件(即安全元件 ATECC608A)的跨平台I2C驱动程序,使用纯Rust编写。

3个版本 (1个稳定版)

1.0.0 2020年9月19日
0.1.1 2020年8月27日
0.1.0 2020年8月27日

#744嵌入式开发

每月45次下载

MIT/Apache

1.5MB
2K SLoC

适用于Microchip加密认证设备(即ATECC608a)的跨平台I2C驱动程序,完全使用Rust编写。此库实现了与Microchip安全设备ATECC608a通信所需的API。

NRF52840 communicating with a ATECC608a

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命令。

功能

  1. 100%安全的Rust代码,即驱动程序中没有内存安全错误(除非我的逻辑错误)。
  2. 平台无关,即对所有硬件依赖项使用'embedded-hal'。
  3. 根本不需要(堆栈)动态内存分配。使用heaplessPostcard进行命令数据包构造。
  4. 与Microchip的CrypoAuthlib 'C库'的API兼容,即使用相同的名称和参数,使其更容易绑定到现有的C代码库。

注意事项

  1. 这个驱动程序是我对“学习语言”感兴趣的结果。所以,它并不完美(或生产质量),可能还有更好的做事方式。欢迎反馈、评论和建议。
  2. 使用'Postcard'将Rust结构体序列化或反序列化为'heapless Vec'(即无_std环境的栈向量)。
  3. 此代码已在nrf板使用'nrf-hal-common'进行测试。理论上,它应该与实现'embedded-hal'特质的任何HAL一起工作。
  4. 在开发过程中,我在nrf52840的i2c/TWIM实现(在nrf52840_hal中)中发现了错误。
  5. 已提交PR并合并了修复,但请确保拉取修复或相应地编辑cargo.toml,以便使此驱动程序正常工作。https://github.com/nrf-rs/nrf-hal/pull/166
  6. 构建此驱动程序rust版本的参考。

用法


#![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字节,分为以下区域

  1. 配置区域:128字节包含设备配置信息,例如每个槽位的访问策略、序列号、锁定信息等。
  2. 数据区域:1208字节(分为16个通用只读或读写内存槽)。
  3. OTP区域:64字节

在我们开始之前,我们需要使用将确定每个数据槽响应的访问策略的值来编程Config区域。配置区域可以修改,直到它被锁定(LockConfig设置为不为0x55)。为了启用访问策略,必须设置LockValue字节。以下是出厂配置与示例(ATECC-TFLXTLS)配置的比较。(示例配置来自Microchip。它用于其一些预配置变体。)

atecc608a_configs_graphical_view

将ATECC-TFLXTLS配置写入设备将产生一个个性化设备,即您现在可以生成/存储密钥、证书和其他内容在设备的EEPROM槽中,如下表所示。有关每个槽位访问策略和可以在此槽上使用的命令的更详细视图,请参阅详细的槽位访问策略

槽位 用例
0 主私钥
1 内部签名私钥
2 次要私钥1
3 次要私钥2
4 次要私钥3
5 秘密密钥
6 IO保护密钥
7 安全启动摘要
8 通用数据
9 AES密钥
10 设备压缩证书
11 签名者公钥
12 签名者压缩证书
13 父公共密钥或通用数据
14 验证过的公共密钥
15 安全启动公共密钥

重要:在继续之前,请阅读本节

  1. 要将ATECC-TFLXTLS配置字节写入设备,请使用示例文件atcab_write_config_zone.rs,该文件包含在示例文件夹中。
  2. 使用读取命令示例 - atcab_read_config_zone.rs读取配置区域的内容(即所有128个字节)。这只是双重检查,以确保正确的字节集(即ATECC-TFLXTLS配置字节)已写入设备。
  3. 在我们开始填充(即从或写入)数据或OTP区域之前,我们必须锁定配置区域。使用atcab_lock_config_zone_crc.rs示例锁定配置区域。注意:锁定配置区域是一个不可逆的操作。因此,请在进行之前确保一切正确。
  4. 完成此操作后,您可以开始使用以下表格中所示的选择将私有的ECC密钥或外部生成的公钥填充设备的数据区域。
    • 示例atcab_sign_and_verify.rs正是如此。它生成并加载一个新(随机)的ECC私钥到设备的3号槽位。私钥在设备内部生成且从未离开。示例包含一个测试消息,该消息由私钥签名并由相应的“计算”公钥验证。
    • 注意:在第3步中,您可以为所有私钥槽位(0-4)执行此操作,因为并非所有访问策略都严格实施。例如,槽位0-1中的私钥是永久的,而槽位2-4是可更新的,根据策略。但是,因为数据区域未锁定,所以像WRITEGENKEY这样的命令仍然可以在没有限制的情况下工作,即使槽位配置为(永久)不可写入。
    • 换句话说,这是您实际上将所有槽位填充到设备中的密钥、证书和其他数据的地方,当您对数据区域的内容满意时,请转到第4步。
  5. 只有锁定数据区域(即配置区域的字节[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许可证中定义,均应双重许可,如上所述,无需任何附加条款或条件。

依赖项