#apdu #ledger #ledger-hardware #protocols #hardware-wallet #encode-decode

no-std ledger-proto

Ledger硬件钱包协议/ APDU定义

1个不稳定版本

0.1.0 2023年6月16日

#1298硬件支持

Download history 15/week @ 2024-03-11 77/week @ 2024-03-18 21/week @ 2024-03-25 102/week @ 2024-04-01 23/week @ 2024-04-08 32/week @ 2024-04-15 10/week @ 2024-04-22 34/week @ 2024-04-29 25/week @ 2024-05-06 37/week @ 2024-05-13 183/week @ 2024-05-20 104/week @ 2024-05-27 144/week @ 2024-06-03 7/week @ 2024-06-10 26/week @ 2024-06-17 24/week @ 2024-06-24

每月下载量:202
3 crates 中使用

Apache-2.0

19KB
327

Ledger硬件钱包APDU特性和共享类型。

这提供了对APDU进行编码和解码的抽象,以支持与Ledger设备的交互。

APDUs必须实现[ApduBase],以及实现encdec::Encodeencdec::Decode(或encdec::DecodeOwned)以支持二进制序列化,命令通过[ApduReq]提供头部信息。可以使用encdec宏自动推导encdec::Encodeencdec::Decode,或者手动在现有对象/编码上实现。

提供了一个[ApduStatic]辅助器来自动实现APDU请求的[ApduReq],并提供了一个通用的[ApduError]类型,以统一APDU对象之间的序列化和反序列化错误。

示例

使用[ApduStatic]的命令APDU(无主体)

use ledger_proto::{ApduStatic, ApduError, Encode, DecodeOwned};

/// Application information request APDU
#[derive(Clone, Debug, PartialEq, Encode, DecodeOwned)]
#[encdec(error = "ApduError")]
pub struct AppInfoReq {}

/// Set CLA and INS values for [AppInfoReq]
impl ApduStatic for AppInfoReq {
    /// Application Info GET APDU is class `0xb0`
    const CLA: u8 = 0xb0;
    /// Application Info GET APDU is instruction `0x00`
    const INS: u8 = 0x01;
}

手动实现响应APDU

use ledger_proto::{ApduStatic, ApduError, Encode, Decode};

/// Example response APDU
#[derive(Clone, Debug, PartialEq)]
pub struct StringResp<'a> {
    pub value: &'a str,
}

/// [Encode] implementation for [StringResp]
impl <'a> Encode for StringResp<'a> {
  type Error = ApduError;

  /// Fetch encoded length
  fn encode_len(&self) -> Result<usize, Self::Error> {
      Ok(1 + self.value.as_bytes().len())
  }

  /// Encode to bytes
  fn encode(&self, buff: &mut [u8]) -> Result<usize, Self::Error> {
    let b = self.value.as_bytes();

    // Check buffer length is valid
    if buff.len() < self.encode_len()?
        || b.len() > u8::MAX as usize {
      return Err(ApduError::InvalidLength);
    }

    // Write value length
    buff[0] = b.len() as u8;

    // Write value
    buff[1..][..b.len()]
        .copy_from_slice(b);

    Ok(1 + b.len())
  }
}

impl <'a> Decode<'a> for StringResp<'a> {
   type Output = Self;
   type Error = ApduError;

    fn decode(buff: &'a [u8]) -> Result<(Self::Output, usize), Self::Error> {
        // Check buffer length
        if buff.len() < 1 {
            return Err(ApduError::InvalidLength);
        }
        let n = buff[0]as usize;
        if n + 1 > buff.len() {
            return Err(ApduError::InvalidLength);
        }

        // Parse string value
        let s = match core::str::from_utf8(&buff[1..][..n]) {
            Ok(v) => v,
            Err(_) => return Err(ApduError::InvalidUtf8),
        };

        // Return object and parsed length
        Ok((Self{ value: s}, n + 1))
   }
}

有关更多示例,请参阅[apdus]模块中提供的共享APDU。

依赖关系

约2MB
约50K SLoC