Crate ledger_proto

source ·
Expand description

Ledger Hardware Wallet APDU traits and shared types.

This provides abstractions for encoding and decoding APDUs for to support interaction with Ledger devices.

APDUs must implement ApduBase as well as encdec::Encode and encdec::Decode (or encdec::DecodeOwned) for binary serialisation, with commands providing header information via ApduReq. encdec::Encode and encdec::Decode can be automatically derived using encdec macros, or manually implemented over existing objects / encodings.

An ApduStatic helper is provided to automatically implement ApduReq for APDU requests with static headers and a common ApduError type is provided to unify serialisation and deserialisation errors across APDU objects.

Examples

Command APDU (no body) using ApduStatic:

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;
}

Manual response APDU implementation

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))
   }
}

For more examples, see the shared APDUs provided in the apdus module.

Modules

  • Ledger common APDU definitions

Structs

  • APDU command header
  • Generic APDU object (enabled with alloc feature), prefer use of strict APDU types where possible

Enums

Traits

  • Generic APDU base trait, auto-implemented where T: EncDec<'a, ApduError>
  • Generic APDU request trait
  • Helper trait for defining static APDU commands, automatically implements ApduReq.
  • Decode trait implemented for binary decodable objects
  • Decode trait implemented for owned types
  • Composite trait requiring an object is reversibly encodable and decodable into borrowed types, useful for simplifying type bounds / generics.
  • Encode trait implemented for binary encodable objects

Derive Macros