ledger_proto/apdus/
app_info.rs

1use encdec::{Decode, Encode};
2
3use crate::{ApduError, ApduStatic};
4
5/// Application information request APDU
6#[derive(Clone, Debug, PartialEq, Encode, Decode)]
7#[encdec(error = "ApduError")]
8pub struct AppInfoReq {}
9
10/// Set CLA and INS values for [AppInfoReq]
11impl ApduStatic for AppInfoReq {
12    /// Application Info GET APDU is class `0xb0`
13    const CLA: u8 = 0xb0;
14
15    /// Application Info GET APDU is instruction `0x00`
16    const INS: u8 = 0x01;
17}
18
19/// Application information response APDU
20#[derive(Debug, PartialEq)]
21pub struct AppInfoResp<'a> {
22    /// Application name
23    pub name: &'a str,
24    /// Application version
25    pub version: &'a str,
26    /// Application flags
27    pub flags: AppFlags,
28}
29
30bitflags::bitflags! {
31    /// Application info flags
32    #[derive(Clone, Debug, PartialEq)]
33    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34    pub struct AppFlags: u8 {
35        /// Recovery mode
36        const RECOVERY = 0x01;
37        /// Signed application
38        const SIGNED = 0x02;
39        /// User onboarded
40        const ONBOARDED = 0x04;
41        /// PIN validated
42        const PIN_VALIDATED = 0xF0;
43    }
44}
45
46impl<'a> AppInfoResp<'a> {
47    /// Create a new application version APDU
48    pub fn new(name: &'a str, version: &'a str, flags: AppFlags) -> Self {
49        Self {
50            name,
51            version,
52            flags,
53        }
54    }
55}
56
57const APP_VERSION_FMT: u8 = 1;
58
59impl<'a> Encode for AppInfoResp<'a> {
60    type Error = ApduError;
61
62    fn encode_len(&self) -> Result<usize, Self::Error> {
63        let mut len = 0;
64
65        len += 1;
66        len += 1 + self.name.len();
67        len += 1 + self.version.len();
68        len += 2;
69
70        Ok(len)
71    }
72
73    fn encode(&self, buff: &mut [u8]) -> Result<usize, Self::Error> {
74        if buff.len() < self.encode_len()? {
75            return Err(ApduError::InvalidLength);
76        }
77
78        let mut index = 0;
79        buff[0] = APP_VERSION_FMT;
80        index += 1;
81
82        buff[index] = self.name.len() as u8;
83        buff[index + 1..][..self.name.len()].copy_from_slice(self.name.as_bytes());
84        index += 1 + self.name.len();
85
86        buff[index] = self.version.len() as u8;
87        buff[index + 1..][..self.version.len()].copy_from_slice(self.version.as_bytes());
88        index += 1 + self.version.len();
89
90        buff[index] = 1;
91        buff[index + 1] = self.flags.bits();
92        index += 2;
93
94        Ok(index)
95    }
96}
97
98impl<'a> Decode<'a> for AppInfoResp<'a> {
99    type Output = Self;
100
101    type Error = ApduError;
102
103    fn decode(buff: &'a [u8]) -> Result<(Self::Output, usize), Self::Error> {
104        let mut index = 0;
105        let buff = buff;
106
107        // Check app version format
108        if buff[index] != APP_VERSION_FMT {
109            return Err(ApduError::InvalidVersion(buff[index]));
110        }
111        index += 1;
112
113        // Fetch name string
114        let name_len = buff[index] as usize;
115        let name = core::str::from_utf8(&buff[index + 1..][..name_len])
116            .map_err(|_| ApduError::InvalidUtf8)?;
117        index += 1 + name_len;
118
119        // Fetch version string
120        let version_len = buff[index] as usize;
121        let version = core::str::from_utf8(&buff[index + 1..][..version_len])
122            .map_err(|_| ApduError::InvalidUtf8)?;
123        index += 1 + version_len;
124
125        // Fetch flags
126        let flags_len = buff[index];
127        let flags = AppFlags::from_bits_truncate(buff[index + 1]);
128        index += 1 + flags_len as usize;
129
130        Ok((
131            Self {
132                name,
133                version,
134                flags,
135            },
136            index,
137        ))
138    }
139}
140
141#[cfg(test)]
142mod tests {
143    use super::*;
144
145    #[test]
146    fn app_info_resp() {
147        let r = AppInfoResp::new("test name", "test version", AppFlags::ONBOARDED);
148
149        let mut buff = [0u8; 256];
150        crate::tests::encode_decode(&mut buff, r);
151    }
152}