1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
// Copyright (c) 2022-2023 The MobileCoin Foundation

//! Application Information APDUs

use encdec::{Decode, DecodeOwned, Encode};

use super::{ApduError, ApduStatic, Instruction, MOB_APDU_CLA};

/// Fetch application info APDU
#[derive(Copy, Clone, PartialEq, Debug, Default)]
pub struct AppInfoReq {}

impl ApduStatic for AppInfoReq {
    /// Application Info command APDU is class `0xb0`
    const CLA: u8 = MOB_APDU_CLA;

    /// Application Info GET APDU is instruction `0x00`
    const INS: u8 = Instruction::GetAppInfo as u8;
}

impl Encode for AppInfoReq {
    type Error = ApduError;

    fn encode_len(&self) -> Result<usize, Self::Error> {
        Ok(0)
    }

    fn encode(&self, _buff: &mut [u8]) -> Result<usize, Self::Error> {
        Ok(0)
    }
}

impl DecodeOwned for AppInfoReq {
    type Output = Self;

    type Error = ApduError;

    fn decode_owned(_buff: &[u8]) -> Result<(Self::Output, usize), Self::Error> {
        Ok((Self {}, 0))
    }
}

/// Application information response APDU
///
/// ## Encoding
///
/// ```text
///  0                   1                   2                   3
///  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// |   PROTO_VER   |   NAME_LEN    |  VERSION_LEN  |   FLAGS_LEN   |
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// /                             NAME...                           /
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// /                            VERSION...                         /
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// /                             FLAGS...                          /
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// ```
///
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct AppInfoResp<'a> {
    /// Protocol version (must be 1)
    pub proto: u8,

    /// Application name
    pub name: &'a str,

    /// Application version
    pub version: &'a str,

    /// Application flags
    pub flags: AppFlags,
}

bitflags::bitflags! {
    /// Application info flags
    pub struct AppFlags: u16 {
        /// Indicates app is unlocked for key requests
        const UNLOCKED = 1 << 0;

        /// Indicates app has tx summary feature
        const HAS_TX_SUMMARY = 1 << 8;
    }
}

impl<'a> AppInfoResp<'a> {
    /// Create a new application version APDU
    pub fn new(proto: u8, name: &'a str, version: &'a str, flags: AppFlags) -> Self {
        Self {
            proto,
            name,
            version,
            flags,
        }
    }
}

impl<'a> Encode for AppInfoResp<'a> {
    type Error = ApduError;

    /// Encode an app version APDU into the provided buffer
    fn encode(&self, buff: &mut [u8]) -> Result<usize, ApduError> {
        let mut index = 0;

        // Check buffer length is viable (MOB-06.6)
        if buff.len() < self.encode_len()? {
            return Err(ApduError::InvalidLength);
        }

        // Set header
        buff[0] = self.proto;
        buff[1] = self.name.len() as u8;
        buff[2] = self.version.len() as u8;
        buff[3] = self.flags.encode_len()? as u8;
        index += 4;

        // Write name
        buff[index..][..self.name.len()].copy_from_slice(self.name.as_bytes());
        index += self.name.len();

        // Write version
        buff[index..][..self.version.len()].copy_from_slice(self.version.as_bytes());
        index += self.version.len();

        // Write flags
        index += self.flags.encode(&mut buff[index..])?;

        Ok(index)
    }

    /// Compute APDU encoded length
    fn encode_len(&self) -> Result<usize, ApduError> {
        let mut len = 4;

        len += self.name.len();
        len += self.version.len();
        len += self.flags.encode_len()?;

        Ok(len)
    }
}

impl<'a> Decode<'a> for AppInfoResp<'a> {
    type Output = Self;
    type Error = ApduError;

    /// Decode an app version APDU from the provided buffer
    fn decode(buff: &'a [u8]) -> Result<(Self, usize), ApduError> {
        let mut index = 0;

        // Check buffer length prior to header parsing (MOB-06.7)
        if buff.len() < 4 {
            return Err(ApduError::InvalidLength);
        }

        // Fetch headers
        let proto = buff[0];
        let name_len = buff[1] as usize;
        let version_len = buff[2] as usize;
        let flags_len = buff[3] as usize;
        index += 4;

        // Check full buffer length (MOB-06.7)
        if buff.len() < 4 + name_len + version_len + flags_len {
            return Err(ApduError::InvalidLength);
        }

        // Fetch name string
        let name =
            core::str::from_utf8(&buff[index..][..name_len]).map_err(|_| ApduError::InvalidUtf8)?;
        index += name_len;

        // Fetch version string
        let version = core::str::from_utf8(&buff[index..][..version_len])
            .map_err(|_| ApduError::InvalidUtf8)?;
        index += version_len;

        // Fetch flags
        let (flags, n) = AppFlags::decode_owned(&buff[index..][..flags_len])?;
        index += n;

        Ok((
            Self {
                proto,
                name,
                version,
                flags,
            },
            index,
        ))
    }
}

impl Encode for AppFlags {
    type Error = ApduError;

    fn encode_len(&self) -> Result<usize, Self::Error> {
        Ok(3)
    }

    fn encode(&self, buff: &mut [u8]) -> Result<usize, Self::Error> {
        // Check buffer size
        if buff.len() < 3 {
            return Err(ApduError::InvalidLength);
        }

        // Set flags length
        buff[0] = 2;

        // Write actual flags
        let b = self.bits().to_le_bytes();
        buff[1..][..2].copy_from_slice(&b);

        // Return length
        Ok(3)
    }
}

impl DecodeOwned for AppFlags {
    type Output = Self;

    type Error = ApduError;

    fn decode_owned(buff: &[u8]) -> Result<(Self::Output, usize), Self::Error> {
        // Check buffer size
        if buff.len() < 2 {
            return Err(ApduError::InvalidLength);
        }

        // Check flags length matches
        let len = buff[0];
        if len != 2 {
            return Err(ApduError::InvalidEncoding);
        }

        // Decode flags
        let bits = u16::from_le_bytes([buff[1], buff[2]]);
        let flags = AppFlags::from_bits_truncate(bits);

        // Return decoded flags and length
        Ok((flags, 3))
    }
}

#[cfg(test)]
mod test {

    use super::*;
    use crate::test::encode_decode_apdu;

    #[test]
    fn app_info_req_apdu() {
        let apdu = AppInfoReq::default();

        let mut buff = [0u8; 128];
        encode_decode_apdu(&mut buff, &apdu);
    }

    #[test]
    fn app_info_resp_apdu() {
        let name = "TEST NAME";
        let version = "TEST VERSION";

        let apdu = AppInfoResp::new(1, name, version, AppFlags::UNLOCKED);

        let mut buff = [0u8; 128];
        encode_decode_apdu(&mut buff, &apdu);
    }
}