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};
#[derive(Clone, Debug, PartialEq, Encode, DecodeOwned)]
#[encdec(error = "ApduError")]
pub struct AppInfoReq {}
impl ApduStatic for AppInfoReq {
const CLA: u8 = 0xb0;
const INS: u8 = 0x01;
}
Manual response APDU implementation
use ledger_proto::{ApduStatic, ApduError, Encode, Decode};
#[derive(Clone, Debug, PartialEq)]
pub struct StringResp<'a> {
pub value: &'a str,
}
impl <'a> Encode for StringResp<'a> {
type Error = ApduError;
fn encode_len(&self) -> Result<usize, Self::Error> {
Ok(1 + self.value.as_bytes().len())
}
fn encode(&self, buff: &mut [u8]) -> Result<usize, Self::Error> {
let b = self.value.as_bytes();
if buff.len() < self.encode_len()?
|| b.len() > u8::MAX as usize {
return Err(ApduError::InvalidLength);
}
buff[0] = b.len() as u8;
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> {
if buff.len() < 1 {
return Err(ApduError::InvalidLength);
}
let n = buff[0]as usize;
if n + 1 > buff.len() {
return Err(ApduError::InvalidLength);
}
let s = match core::str::from_utf8(&buff[1..][..n]) {
Ok(v) => v,
Err(_) => return Err(ApduError::InvalidUtf8),
};
Ok((Self{ value: s}, n + 1))
}
}
For more examples, see the shared APDUs provided in the [apdus] module.