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§

apdus
Ledger common APDU definitions

Structs§

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

Enums§

ApduError
APDU error type

Traits§

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

Derive Macros§

Decode
#[derive(Decode)] support.
DecodeOwned
#[derive(DecodeOwned)] support.
Encode
#[derive(Encode)] support.