ledger_proto/apdus/
app_info.rs1use encdec::{Decode, Encode};
2
3use crate::{ApduError, ApduStatic};
4
5#[derive(Clone, Debug, PartialEq, Encode, Decode)]
7#[encdec(error = "ApduError")]
8pub struct AppInfoReq {}
9
10impl ApduStatic for AppInfoReq {
12 const CLA: u8 = 0xb0;
14
15 const INS: u8 = 0x01;
17}
18
19#[derive(Debug, PartialEq)]
21pub struct AppInfoResp<'a> {
22 pub name: &'a str,
24 pub version: &'a str,
26 pub flags: AppFlags,
28}
29
30bitflags::bitflags! {
31 #[derive(Clone, Debug, PartialEq)]
33 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34 pub struct AppFlags: u8 {
35 const RECOVERY = 0x01;
37 const SIGNED = 0x02;
39 const ONBOARDED = 0x04;
41 const PIN_VALIDATED = 0xF0;
43 }
44}
45
46impl<'a> AppInfoResp<'a> {
47 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 if buff[index] != APP_VERSION_FMT {
109 return Err(ApduError::InvalidVersion(buff[index]));
110 }
111 index += 1;
112
113 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 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 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}