667 lines
19 KiB
Rust
667 lines
19 KiB
Rust
//! 硬件钱包集成模块
|
|
//!
|
|
//! 实现Ledger和Trezor硬件钱包支持、通信协议和设备管理
|
|
|
|
use crate::WalletError;
|
|
use std::collections::HashMap;
|
|
|
|
/// 硬件钱包类型
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub enum HardwareWalletType {
|
|
/// Ledger Nano S
|
|
LedgerNanoS,
|
|
/// Ledger Nano X
|
|
LedgerNanoX,
|
|
/// Trezor One
|
|
TrezorOne,
|
|
/// Trezor Model T
|
|
TrezorModelT,
|
|
}
|
|
|
|
impl HardwareWalletType {
|
|
/// 获取设备名称
|
|
pub fn name(&self) -> &'static str {
|
|
match self {
|
|
HardwareWalletType::LedgerNanoS => "Ledger Nano S",
|
|
HardwareWalletType::LedgerNanoX => "Ledger Nano X",
|
|
HardwareWalletType::TrezorOne => "Trezor One",
|
|
HardwareWalletType::TrezorModelT => "Trezor Model T",
|
|
}
|
|
}
|
|
|
|
/// 是否为Ledger设备
|
|
pub fn is_ledger(&self) -> bool {
|
|
matches!(self, HardwareWalletType::LedgerNanoS | HardwareWalletType::LedgerNanoX)
|
|
}
|
|
|
|
/// 是否为Trezor设备
|
|
pub fn is_trezor(&self) -> bool {
|
|
matches!(self, HardwareWalletType::TrezorOne | HardwareWalletType::TrezorModelT)
|
|
}
|
|
}
|
|
|
|
/// 设备连接状态
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum DeviceStatus {
|
|
/// 已连接
|
|
Connected,
|
|
/// 已断开
|
|
Disconnected,
|
|
/// 锁定中
|
|
Locked,
|
|
/// 错误
|
|
Error,
|
|
}
|
|
|
|
/// 硬件钱包设备
|
|
#[derive(Debug, Clone)]
|
|
pub struct HardwareDevice {
|
|
/// 设备ID
|
|
pub id: String,
|
|
/// 设备类型
|
|
pub device_type: HardwareWalletType,
|
|
/// 固件版本
|
|
pub firmware_version: String,
|
|
/// 序列号
|
|
pub serial_number: String,
|
|
/// 连接状态
|
|
pub status: DeviceStatus,
|
|
/// 支持的币种
|
|
pub supported_coins: Vec<String>,
|
|
}
|
|
|
|
impl HardwareDevice {
|
|
/// 创建新设备
|
|
pub fn new(
|
|
id: String,
|
|
device_type: HardwareWalletType,
|
|
firmware_version: String,
|
|
serial_number: String
|
|
) -> Self {
|
|
let supported_coins = match device_type {
|
|
HardwareWalletType::LedgerNanoS | HardwareWalletType::LedgerNanoX => {
|
|
vec!["BTC".to_string(), "ETH".to_string(), "NAC".to_string()]
|
|
}
|
|
HardwareWalletType::TrezorOne | HardwareWalletType::TrezorModelT => {
|
|
vec!["BTC".to_string(), "ETH".to_string(), "NAC".to_string()]
|
|
}
|
|
};
|
|
|
|
HardwareDevice {
|
|
id,
|
|
device_type,
|
|
firmware_version,
|
|
serial_number,
|
|
status: DeviceStatus::Disconnected,
|
|
supported_coins,
|
|
}
|
|
}
|
|
|
|
/// 检查是否支持币种
|
|
pub fn supports_coin(&self, coin: &str) -> bool {
|
|
self.supported_coins.iter().any(|c| c == coin)
|
|
}
|
|
}
|
|
|
|
/// 通信协议
|
|
pub trait CommunicationProtocol {
|
|
/// 连接设备
|
|
fn connect(&mut self) -> Result<(), WalletError>;
|
|
|
|
/// 断开设备
|
|
fn disconnect(&mut self) -> Result<(), WalletError>;
|
|
|
|
/// 发送命令
|
|
fn send_command(&self, command: &[u8]) -> Result<Vec<u8>, WalletError>;
|
|
|
|
/// 获取设备信息
|
|
fn get_device_info(&self) -> Result<HardwareDevice, WalletError>;
|
|
}
|
|
|
|
/// Ledger通信协议
|
|
pub struct LedgerProtocol {
|
|
device_id: String,
|
|
connected: bool,
|
|
}
|
|
|
|
impl LedgerProtocol {
|
|
/// 创建新的Ledger协议实例
|
|
pub fn new(device_id: String) -> Self {
|
|
LedgerProtocol {
|
|
device_id,
|
|
connected: false,
|
|
}
|
|
}
|
|
|
|
/// Ledger APDU命令
|
|
fn apdu_command(&self, cla: u8, ins: u8, p1: u8, p2: u8, data: &[u8]) -> Vec<u8> {
|
|
let mut command = vec![cla, ins, p1, p2, data.len() as u8];
|
|
command.extend_from_slice(data);
|
|
command
|
|
}
|
|
}
|
|
|
|
impl CommunicationProtocol for LedgerProtocol {
|
|
fn connect(&mut self) -> Result<(), WalletError> {
|
|
// 模拟连接
|
|
self.connected = true;
|
|
Ok(())
|
|
}
|
|
|
|
fn disconnect(&mut self) -> Result<(), WalletError> {
|
|
self.connected = false;
|
|
Ok(())
|
|
}
|
|
|
|
fn send_command(&self, command: &[u8]) -> Result<Vec<u8>, WalletError> {
|
|
if !self.connected {
|
|
return Err(WalletError::NetworkError("Device not connected".to_string()));
|
|
}
|
|
|
|
// 模拟命令响应
|
|
Ok(vec![0x90, 0x00]) // 成功响应
|
|
}
|
|
|
|
fn get_device_info(&self) -> Result<HardwareDevice, WalletError> {
|
|
if !self.connected {
|
|
return Err(WalletError::NetworkError("Device not connected".to_string()));
|
|
}
|
|
|
|
Ok(HardwareDevice::new(
|
|
self.device_id.clone(),
|
|
HardwareWalletType::LedgerNanoX,
|
|
"2.1.0".to_string(),
|
|
"LEDGER123456".to_string()
|
|
))
|
|
}
|
|
}
|
|
|
|
/// Trezor通信协议
|
|
pub struct TrezorProtocol {
|
|
device_id: String,
|
|
connected: bool,
|
|
}
|
|
|
|
impl TrezorProtocol {
|
|
/// 创建新的Trezor协议实例
|
|
pub fn new(device_id: String) -> Self {
|
|
TrezorProtocol {
|
|
device_id,
|
|
connected: false,
|
|
}
|
|
}
|
|
|
|
/// Trezor消息编码
|
|
fn encode_message(&self, message_type: u16, data: &[u8]) -> Vec<u8> {
|
|
let mut encoded = vec![0x23, 0x23]; // 魔术头
|
|
encoded.extend_from_slice(&message_type.to_be_bytes());
|
|
encoded.extend_from_slice(&(data.len() as u32).to_be_bytes());
|
|
encoded.extend_from_slice(data);
|
|
encoded
|
|
}
|
|
}
|
|
|
|
impl CommunicationProtocol for TrezorProtocol {
|
|
fn connect(&mut self) -> Result<(), WalletError> {
|
|
self.connected = true;
|
|
Ok(())
|
|
}
|
|
|
|
fn disconnect(&mut self) -> Result<(), WalletError> {
|
|
self.connected = false;
|
|
Ok(())
|
|
}
|
|
|
|
fn send_command(&self, command: &[u8]) -> Result<Vec<u8>, WalletError> {
|
|
if !self.connected {
|
|
return Err(WalletError::NetworkError("Device not connected".to_string()));
|
|
}
|
|
|
|
// 模拟命令响应
|
|
Ok(vec![0x00, 0x01]) // 成功响应
|
|
}
|
|
|
|
fn get_device_info(&self) -> Result<HardwareDevice, WalletError> {
|
|
if !self.connected {
|
|
return Err(WalletError::NetworkError("Device not connected".to_string()));
|
|
}
|
|
|
|
Ok(HardwareDevice::new(
|
|
self.device_id.clone(),
|
|
HardwareWalletType::TrezorModelT,
|
|
"2.4.0".to_string(),
|
|
"TREZOR789012".to_string()
|
|
))
|
|
}
|
|
}
|
|
|
|
/// 硬件钱包操作
|
|
pub trait HardwareWalletOps {
|
|
/// 获取公钥
|
|
fn get_public_key(&self, path: &str) -> Result<Vec<u8>, WalletError>;
|
|
|
|
/// 签名交易
|
|
fn sign_transaction(&self, path: &str, transaction: &[u8]) -> Result<Vec<u8>, WalletError>;
|
|
|
|
/// 签名消息
|
|
fn sign_message(&self, path: &str, message: &[u8]) -> Result<Vec<u8>, WalletError>;
|
|
|
|
/// 验证地址
|
|
fn verify_address(&self, path: &str, address: &str) -> Result<bool, WalletError>;
|
|
}
|
|
|
|
/// Ledger钱包操作
|
|
pub struct LedgerWallet {
|
|
protocol: LedgerProtocol,
|
|
device: Option<HardwareDevice>,
|
|
}
|
|
|
|
impl LedgerWallet {
|
|
/// 创建新的Ledger钱包
|
|
pub fn new(device_id: String) -> Self {
|
|
LedgerWallet {
|
|
protocol: LedgerProtocol::new(device_id),
|
|
device: None,
|
|
}
|
|
}
|
|
|
|
/// 连接设备
|
|
pub fn connect(&mut self) -> Result<(), WalletError> {
|
|
self.protocol.connect()?;
|
|
self.device = Some(self.protocol.get_device_info()?);
|
|
Ok(())
|
|
}
|
|
|
|
/// 断开设备
|
|
pub fn disconnect(&mut self) -> Result<(), WalletError> {
|
|
self.protocol.disconnect()?;
|
|
self.device = None;
|
|
Ok(())
|
|
}
|
|
|
|
/// 获取设备信息
|
|
pub fn device(&self) -> Option<&HardwareDevice> {
|
|
self.device.as_ref()
|
|
}
|
|
}
|
|
|
|
impl HardwareWalletOps for LedgerWallet {
|
|
fn get_public_key(&self, path: &str) -> Result<Vec<u8>, WalletError> {
|
|
if self.device.is_none() {
|
|
return Err(WalletError::NetworkError("Device not connected".to_string()));
|
|
}
|
|
|
|
// 构造获取公钥命令
|
|
let command = vec![0x80, 0x02, 0x00, 0x00, path.len() as u8];
|
|
let _response = self.protocol.send_command(&command)?;
|
|
|
|
// 模拟返回公钥
|
|
Ok(vec![0x04; 65]) // 未压缩公钥
|
|
}
|
|
|
|
fn sign_transaction(&self, path: &str, transaction: &[u8]) -> Result<Vec<u8>, WalletError> {
|
|
if self.device.is_none() {
|
|
return Err(WalletError::NetworkError("Device not connected".to_string()));
|
|
}
|
|
|
|
// 构造签名命令
|
|
let mut command = vec![0x80, 0x04, 0x00, 0x00];
|
|
command.push((path.len() + transaction.len()) as u8);
|
|
command.extend_from_slice(path.as_bytes());
|
|
command.extend_from_slice(transaction);
|
|
|
|
let _response = self.protocol.send_command(&command)?;
|
|
|
|
// 模拟返回签名
|
|
Ok(vec![0x30; 71]) // DER编码签名
|
|
}
|
|
|
|
fn sign_message(&self, path: &str, message: &[u8]) -> Result<Vec<u8>, WalletError> {
|
|
if self.device.is_none() {
|
|
return Err(WalletError::NetworkError("Device not connected".to_string()));
|
|
}
|
|
|
|
// 构造签名消息命令
|
|
let mut command = vec![0x80, 0x08, 0x00, 0x00];
|
|
command.push((path.len() + message.len()) as u8);
|
|
command.extend_from_slice(path.as_bytes());
|
|
command.extend_from_slice(message);
|
|
|
|
let _response = self.protocol.send_command(&command)?;
|
|
|
|
// 模拟返回签名
|
|
Ok(vec![0x30; 71])
|
|
}
|
|
|
|
fn verify_address(&self, path: &str, address: &str) -> Result<bool, WalletError> {
|
|
if self.device.is_none() {
|
|
return Err(WalletError::NetworkError("Device not connected".to_string()));
|
|
}
|
|
|
|
// 获取公钥并验证地址
|
|
let _public_key = self.get_public_key(path)?;
|
|
|
|
// 模拟地址验证
|
|
Ok(address.starts_with("0x"))
|
|
}
|
|
}
|
|
|
|
/// Trezor钱包操作
|
|
pub struct TrezorWallet {
|
|
protocol: TrezorProtocol,
|
|
device: Option<HardwareDevice>,
|
|
}
|
|
|
|
impl TrezorWallet {
|
|
/// 创建新的Trezor钱包
|
|
pub fn new(device_id: String) -> Self {
|
|
TrezorWallet {
|
|
protocol: TrezorProtocol::new(device_id),
|
|
device: None,
|
|
}
|
|
}
|
|
|
|
/// 连接设备
|
|
pub fn connect(&mut self) -> Result<(), WalletError> {
|
|
self.protocol.connect()?;
|
|
self.device = Some(self.protocol.get_device_info()?);
|
|
Ok(())
|
|
}
|
|
|
|
/// 断开设备
|
|
pub fn disconnect(&mut self) -> Result<(), WalletError> {
|
|
self.protocol.disconnect()?;
|
|
self.device = None;
|
|
Ok(())
|
|
}
|
|
|
|
/// 获取设备信息
|
|
pub fn device(&self) -> Option<&HardwareDevice> {
|
|
self.device.as_ref()
|
|
}
|
|
}
|
|
|
|
impl HardwareWalletOps for TrezorWallet {
|
|
fn get_public_key(&self, path: &str) -> Result<Vec<u8>, WalletError> {
|
|
if self.device.is_none() {
|
|
return Err(WalletError::NetworkError("Device not connected".to_string()));
|
|
}
|
|
|
|
// 构造获取公钥消息
|
|
let message = self.protocol.encode_message(11, path.as_bytes());
|
|
let _response = self.protocol.send_command(&message)?;
|
|
|
|
// 模拟返回公钥
|
|
Ok(vec![0x04; 65])
|
|
}
|
|
|
|
fn sign_transaction(&self, path: &str, transaction: &[u8]) -> Result<Vec<u8>, WalletError> {
|
|
if self.device.is_none() {
|
|
return Err(WalletError::NetworkError("Device not connected".to_string()));
|
|
}
|
|
|
|
// 构造签名消息
|
|
let mut data = path.as_bytes().to_vec();
|
|
data.extend_from_slice(transaction);
|
|
let message = self.protocol.encode_message(15, &data);
|
|
let _response = self.protocol.send_command(&message)?;
|
|
|
|
// 模拟返回签名
|
|
Ok(vec![0x30; 71])
|
|
}
|
|
|
|
fn sign_message(&self, path: &str, message: &[u8]) -> Result<Vec<u8>, WalletError> {
|
|
if self.device.is_none() {
|
|
return Err(WalletError::NetworkError("Device not connected".to_string()));
|
|
}
|
|
|
|
// 构造签名消息
|
|
let mut data = path.as_bytes().to_vec();
|
|
data.extend_from_slice(message);
|
|
let msg = self.protocol.encode_message(38, &data);
|
|
let _response = self.protocol.send_command(&msg)?;
|
|
|
|
// 模拟返回签名
|
|
Ok(vec![0x30; 71])
|
|
}
|
|
|
|
fn verify_address(&self, path: &str, address: &str) -> Result<bool, WalletError> {
|
|
if self.device.is_none() {
|
|
return Err(WalletError::NetworkError("Device not connected".to_string()));
|
|
}
|
|
|
|
// 获取公钥并验证地址
|
|
let _public_key = self.get_public_key(path)?;
|
|
|
|
// 模拟地址验证
|
|
Ok(address.starts_with("0x"))
|
|
}
|
|
}
|
|
|
|
/// 设备管理器
|
|
pub struct DeviceManager {
|
|
/// 已连接的设备
|
|
devices: HashMap<String, Box<dyn std::any::Any>>,
|
|
/// 设备信息
|
|
device_info: HashMap<String, HardwareDevice>,
|
|
}
|
|
|
|
impl DeviceManager {
|
|
/// 创建新的设备管理器
|
|
pub fn new() -> Self {
|
|
DeviceManager {
|
|
devices: HashMap::new(),
|
|
device_info: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
/// 扫描设备
|
|
pub fn scan_devices(&mut self) -> Result<Vec<HardwareDevice>, WalletError> {
|
|
// 模拟扫描设备
|
|
let devices = vec![
|
|
HardwareDevice::new(
|
|
"ledger_001".to_string(),
|
|
HardwareWalletType::LedgerNanoX,
|
|
"2.1.0".to_string(),
|
|
"LEDGER123456".to_string()
|
|
),
|
|
HardwareDevice::new(
|
|
"trezor_001".to_string(),
|
|
HardwareWalletType::TrezorModelT,
|
|
"2.4.0".to_string(),
|
|
"TREZOR789012".to_string()
|
|
),
|
|
];
|
|
|
|
for device in &devices {
|
|
self.device_info.insert(device.id.clone(), device.clone());
|
|
}
|
|
|
|
Ok(devices)
|
|
}
|
|
|
|
/// 连接Ledger设备
|
|
pub fn connect_ledger(&mut self, device_id: String) -> Result<(), WalletError> {
|
|
let mut wallet = LedgerWallet::new(device_id.clone());
|
|
wallet.connect()?;
|
|
|
|
if let Some(device) = wallet.device() {
|
|
self.device_info.insert(device_id.clone(), device.clone());
|
|
}
|
|
|
|
self.devices.insert(device_id, Box::new(wallet));
|
|
Ok(())
|
|
}
|
|
|
|
/// 连接Trezor设备
|
|
pub fn connect_trezor(&mut self, device_id: String) -> Result<(), WalletError> {
|
|
let mut wallet = TrezorWallet::new(device_id.clone());
|
|
wallet.connect()?;
|
|
|
|
if let Some(device) = wallet.device() {
|
|
self.device_info.insert(device_id.clone(), device.clone());
|
|
}
|
|
|
|
self.devices.insert(device_id, Box::new(wallet));
|
|
Ok(())
|
|
}
|
|
|
|
/// 断开设备
|
|
pub fn disconnect(&mut self, device_id: &str) -> Result<(), WalletError> {
|
|
self.devices.remove(device_id);
|
|
if let Some(device) = self.device_info.get_mut(device_id) {
|
|
device.status = DeviceStatus::Disconnected;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// 获取设备信息
|
|
pub fn get_device(&self, device_id: &str) -> Option<&HardwareDevice> {
|
|
self.device_info.get(device_id)
|
|
}
|
|
|
|
/// 获取所有设备
|
|
pub fn list_devices(&self) -> Vec<&HardwareDevice> {
|
|
self.device_info.values().collect()
|
|
}
|
|
|
|
/// 检查设备是否已连接
|
|
pub fn is_connected(&self, device_id: &str) -> bool {
|
|
self.devices.contains_key(device_id)
|
|
}
|
|
}
|
|
|
|
impl Default for DeviceManager {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_hardware_wallet_type() {
|
|
let ledger = HardwareWalletType::LedgerNanoX;
|
|
assert!(ledger.is_ledger());
|
|
assert!(!ledger.is_trezor());
|
|
|
|
let trezor = HardwareWalletType::TrezorModelT;
|
|
assert!(trezor.is_trezor());
|
|
assert!(!trezor.is_ledger());
|
|
}
|
|
|
|
#[test]
|
|
fn test_hardware_device_creation() {
|
|
let device = HardwareDevice::new(
|
|
"test_001".to_string(),
|
|
HardwareWalletType::LedgerNanoX,
|
|
"2.1.0".to_string(),
|
|
"SERIAL123".to_string()
|
|
);
|
|
assert_eq!(device.id, "test_001");
|
|
assert_eq!(device.status, DeviceStatus::Disconnected);
|
|
assert!(device.supports_coin("NAC"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_ledger_protocol() {
|
|
let mut protocol = LedgerProtocol::new("test_001".to_string());
|
|
assert!(protocol.connect().is_ok());
|
|
assert!(protocol.disconnect().is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_trezor_protocol() {
|
|
let mut protocol = TrezorProtocol::new("test_001".to_string());
|
|
assert!(protocol.connect().is_ok());
|
|
assert!(protocol.disconnect().is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_ledger_wallet() {
|
|
let mut wallet = LedgerWallet::new("test_001".to_string());
|
|
assert!(wallet.connect().is_ok());
|
|
assert!(wallet.device().is_some());
|
|
assert!(wallet.disconnect().is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_trezor_wallet() {
|
|
let mut wallet = TrezorWallet::new("test_001".to_string());
|
|
assert!(wallet.connect().is_ok());
|
|
assert!(wallet.device().is_some());
|
|
assert!(wallet.disconnect().is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_get_public_key() {
|
|
let mut wallet = LedgerWallet::new("test_001".to_string());
|
|
wallet.connect().unwrap();
|
|
let pubkey = wallet.get_public_key("m/44'/60'/0'/0/0").unwrap();
|
|
assert_eq!(pubkey.len(), 65);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sign_transaction() {
|
|
let mut wallet = LedgerWallet::new("test_001".to_string());
|
|
wallet.connect().unwrap();
|
|
let signature = wallet.sign_transaction("m/44'/60'/0'/0/0", &[1, 2, 3]).unwrap();
|
|
assert_eq!(signature.len(), 71);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sign_message() {
|
|
let mut wallet = TrezorWallet::new("test_001".to_string());
|
|
wallet.connect().unwrap();
|
|
let signature = wallet.sign_message("m/44'/60'/0'/0/0", b"Hello").unwrap();
|
|
assert_eq!(signature.len(), 71);
|
|
}
|
|
|
|
#[test]
|
|
fn test_verify_address() {
|
|
let mut wallet = LedgerWallet::new("test_001".to_string());
|
|
wallet.connect().unwrap();
|
|
let valid = wallet.verify_address("m/44'/60'/0'/0/0", "0x1234567890").unwrap();
|
|
assert!(valid);
|
|
}
|
|
|
|
#[test]
|
|
fn test_device_manager() {
|
|
let mut manager = DeviceManager::new();
|
|
let devices = manager.scan_devices().unwrap();
|
|
assert_eq!(devices.len(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_connect_ledger() {
|
|
let mut manager = DeviceManager::new();
|
|
assert!(manager.connect_ledger("ledger_001".to_string()).is_ok());
|
|
assert!(manager.is_connected("ledger_001"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_connect_trezor() {
|
|
let mut manager = DeviceManager::new();
|
|
assert!(manager.connect_trezor("trezor_001".to_string()).is_ok());
|
|
assert!(manager.is_connected("trezor_001"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_disconnect_device() {
|
|
let mut manager = DeviceManager::new();
|
|
manager.connect_ledger("ledger_001".to_string()).unwrap();
|
|
assert!(manager.disconnect("ledger_001").is_ok());
|
|
assert!(!manager.is_connected("ledger_001"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_list_devices() {
|
|
let mut manager = DeviceManager::new();
|
|
manager.scan_devices().unwrap();
|
|
let devices = manager.list_devices();
|
|
assert_eq!(devices.len(), 2);
|
|
}
|
|
}
|