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
- APDU error type
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
#[derive(Decode)]
support.#[derive(DecodeOwned)]
support.#[derive(Encode)]
support.