1 个不稳定版本

0.1.0 2022年8月9日

#6 in #applet

自定义许可

145KB
3K SLoC

Cryptnox Rust通信库

这是一个用于与Cryptnox智能卡小程序一起使用的Rust库。它提供了发送指令与Cryptnox交互以及管理其生命周期的函数。核心模块是cryptnox_rs,它提供了一个连接类,可以用来通过工厂方法初始化卡片实例。

要购买此库支持的NFC卡,请访问: https://www.cryptnox.com/

要求

需要

  • Rust v1.61.0
  • Cargo v1.61.0

安装

在您的 cargo.toml 文件中添加以下行

cryptnox_rs = "*"

要从项目中删除库,请删除相同代码行并运行 cargo build

问题

库使用

要获取卡片,必须与读者的索引建立连接。然后可以将连接传递给工厂,该工厂将初始化一个对象,以从正确的类中为读者的卡片类型和版本创建卡片。

use cryptnox_rs as crs
use crs::enums::FactoryReturn

fn main() {
    println!("Starting test");
    let conn = crs::connection::Connection::new(0).unwrap();
    let card = crs::factory::get_card(conn, false);
    match card{
        FactoryReturn::BG1(bg1) => {
            println!("BG1 card found.");
            let mut card = bg1;
        },
        FactoryReturn::BG0(bg0) => {
            println!("BG0 card found.");
            let mut card = bg0;
        },
        FactoryReturn::NFT(nft) => {
            println!("BG0 card found.");
            let mut card = nft;
        },
    }

工厂将

  • 连接到卡片
  • 选择小程序
  • 读取小程序参数
  • 选择处理卡片的类

卡片包含基本信息

  • card.serial_number : 整数 : 卡片/小程序实例唯一ID
  • card.applet_version : 3个整数列表 : 小程序版本(例如 1.2.2)

初始化和配对

安装后,小程序未初始化,用户需要发送一些参数才能使用卡片。初始化只能执行一次。任何基础参数的更改都需要完整的小程序重新安装(除PIN/PUK更改外)。

初始化后,卡片和PC必须共享一个共同秘密,用作认证的安全通道。此秘密在以后任何时候都需要,以与卡片通信(使用安全通道)。此共同秘密的注册在初始化阶段完成。

所需初始化参数为

  • 名称(最多20个字符的字符串)
  • 电子邮件(最多60个字符的字符串)
  • PIN(9位字符串)
  • PUK(15位字符串)
  • 可选:第一个配对秘密(32字节的字节数组)

PIN

初始化期间选择的PIN需要在每次卡片重置后提供,并打开一个安全通道。

要测试PIN字符串,只需使用

card.verify_pin(pin);

种子管理

应用程序管理一个名为“种子”的256位主密钥。这是BIP32主种子,可以使用BIP39从助记词计算得到二进制种子。然后,使用BIP32派生方案从这个种子计算得到用于ECDSA的密钥对。

种子生成

可以在卡片中使用java芯片系统中的随机数生成器(AIS 20类DRG.3)生成种子。这样,种子密钥永远不会离开卡片保护。

生成新的种子密钥的方法是

card.generate_seed(pin);

恢复

Cryptnox应用程序可以加载二进制种子。

使用此方法在卡片中加载种子

card.load_seed(seed, pin);

种子是32字节。

一旦使用load_seed方法在卡片中加载了这个种子,这张卡片现在就像备份过一样(或那个备份)。请注意,密钥派生路径不是备份,必须完全相同才能检索到相同的密钥对。有关更多详细信息,请参阅下面的派生和密钥系统。

有关恢复的更多详细信息,请参阅API文档中的load_seed操作。

派生和密钥系统

卡片应用程序完全符合BIP32,但主密钥派生的最大深度为8级。可以打开卡片,返回用于需要它的应用程序的扩展公钥。

卡片存储当前密钥对(及其父级),用于签名。可以使用derive方法更改它,也可以在签名命令期间更改,提供相对路径(从当前密钥对),或绝对路径(从主密钥对)。有关更多详细信息,请参阅API文档中的derive方法。

任何派生都会终止任何打开的签名会话,并重置签名认证。生成的密钥用于所有后续签名会话。

从父密钥开始派生的能力允许更有效地在同一个密钥的子代之间切换。然而,请注意,只有当前密钥的直接父级被缓存,因此不能使用它返回密钥层次结构中的上一级。

为了方便使用,用户可以在每次卡片启动时(甚至每个签名之前)从根主节点密钥对(绝对路径)派生。这需要几秒钟。因此,最好存储中间公钥哈希并检查状态以观察正在使用的当前密钥对。如果每天的签名量低于一千,就不需要这种卡片外部的复杂密钥管理。

有关更多信息,请参阅API文档中的derive和sign方法。

EC签名

也可以使用签名命令(相对或绝对)派生密钥对节点。

卡片应用程序可以使用ECDSA和256k1 EC参数对提供的任何256位哈希进行签名。大多数区块链系统使用SHA2-256对消息进行哈希处理,但此卡片应用程序对此不敏感,因为签名是在用户提供的哈希上进行的。请注意,在用户提供EC384签名时,需要事先确认此哈希。

使用EC当前密钥节点进行签名的代码是

let signature = card.sign(data_hash, <u8 value of cryptnox_rs::Derivation::CURRENT_KEY>);

data_hash是包含要使用ECDSA ecp256k1签名的EC哈希的字节序列。

签名是一个字节序列,编码为两个整数值r和s的ASN1 DER序列。

有关更多信息,请参阅API文档中的sign方法。

依赖关系

~12–30MB
~444K SLoC