ledger_proto/apdus/
device_info.rs

1use encdec::{Decode, Encode};
2
3use crate::{ApduError, ApduStatic};
4
5/// Device info APDU command
6#[derive(Copy, Clone, PartialEq, Debug, Default, Encode, Decode)]
7#[encdec(error = "ApduError")]
8pub struct DeviceInfoReq {}
9
10impl ApduStatic for DeviceInfoReq {
11    /// Device info request APDU is class `0xe0`
12    const CLA: u8 = 0xe0;
13
14    /// Device info request APDU is instruction `0x01`
15    const INS: u8 = 0x01;
16}
17
18/// Device info APDU response
19#[derive(Copy, Clone, PartialEq, Debug)]
20pub struct DeviceInfoResp<'a> {
21    /// Target ID
22    pub target_id: [u8; 4],
23
24    /// Secure Element Version
25    pub se_version: &'a str,
26
27    /// Device Flag(s)
28    pub flags: &'a [u8],
29
30    /// MCU Version
31    pub mcu_version: &'a str,
32}
33
34impl<'a> DeviceInfoResp<'a> {
35    /// Create a new device info APDU
36    pub fn new(
37        target_id: [u8; 4],
38        se_version: &'a str,
39        mcu_version: &'a str,
40        flags: &'a [u8],
41    ) -> Self {
42        Self {
43            target_id,
44            se_version,
45            mcu_version,
46            flags,
47        }
48    }
49}
50
51impl<'a> Encode for DeviceInfoResp<'a> {
52    type Error = ApduError;
53
54    /// Encode an device info APDU into the provided buffer
55    fn encode(&self, buff: &mut [u8]) -> Result<usize, ApduError> {
56        // Check buffer length is viable
57        if buff.len() < self.encode_len()? {
58            return Err(ApduError::InvalidLength);
59        }
60
61        let mut index = 0;
62
63        // Write target ID
64        buff[index..][..4].copy_from_slice(&self.target_id);
65        index += 4;
66
67        // Write SE version
68        buff[index] = self.se_version.len() as u8;
69        buff[index + 1..][..self.se_version.len()].copy_from_slice(self.se_version.as_bytes());
70        index += 1 + self.se_version.len();
71
72        // Write flags
73        buff[index] = self.flags.len() as u8;
74        buff[index + 1..][..self.flags.len()].copy_from_slice(self.flags);
75        index += 1 + self.flags.len();
76
77        // Write MCU version
78        buff[index] = self.mcu_version.len() as u8;
79        buff[index + 1..][..self.mcu_version.len()].copy_from_slice(self.mcu_version.as_bytes());
80        index += 1 + self.mcu_version.len();
81
82        Ok(index)
83    }
84
85    /// Compute APDU encoded length
86    fn encode_len(&self) -> Result<usize, ApduError> {
87        let mut len = 4;
88
89        len += 1 + self.se_version.len();
90        len += 1 + self.flags.len();
91        len += 1 + self.mcu_version.len();
92
93        Ok(len)
94    }
95}
96
97impl<'a> Decode<'a> for DeviceInfoResp<'a> {
98    type Output = Self;
99    type Error = ApduError;
100
101    /// Decode an device info APDU from the provided buffer
102    fn decode(buff: &'a [u8]) -> Result<(Self, usize), ApduError> {
103        let mut index = 0;
104        let buff = buff;
105
106        // Fetch target id
107        let mut target_id = [0u8; 4];
108        target_id.copy_from_slice(&buff[..4]);
109        index += 4;
110
111        // Fetch secure element version
112        let se_version_len = buff[index] as usize;
113        let se_version = core::str::from_utf8(&buff[index + 1..][..se_version_len])
114            .map_err(|_| ApduError::InvalidUtf8)?;
115        index += 1 + se_version_len;
116
117        // Fetch flags
118        let flags_len = buff[index] as usize;
119        let flags = &buff[index + 1..][..flags_len];
120        index += 1 + flags_len;
121
122        // Fetch mcu version
123        let mcu_version_len = buff[index] as usize;
124        let mcu_version = core::str::from_utf8(&buff[index + 1..][..mcu_version_len])
125            .map_err(|_| ApduError::InvalidUtf8)?;
126        index += 1 + mcu_version_len;
127
128        Ok((
129            Self {
130                target_id,
131                se_version,
132                flags,
133                mcu_version,
134            },
135            index,
136        ))
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    #[test]
145    fn device_info_resp() {
146        let r = DeviceInfoResp::new([0x01, 0x02, 0x03, 0x04], "SOME SE", "SOME MCU", &[0xaa]);
147
148        let mut buff = [0u8; 256];
149        crate::tests::encode_decode(&mut buff, r);
150    }
151}