openpgp_card/ocard/apdu/
command.rs

1// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Data structure for APDU Commands
5//! (Commands get sent to the card, which will usually send back a `Response`)
6
7use secrecy::ExposeSecret as _;
8use secrecy::SecretVec;
9
10use crate::Error;
11
12#[derive(Clone, Copy)]
13pub enum Expect {
14    Empty,
15    Some,
16    Short(u8),
17}
18
19#[derive(Debug)]
20pub(crate) struct Command {
21    // Class byte (CLA)
22    cla: u8,
23
24    // Instruction byte (INS)
25    ins: u8,
26
27    // Parameter bytes (P1/P2)
28    p1: u8,
29    p2: u8,
30
31    // NOTE: data must be smaller than 64 kbyte
32    data: Data,
33}
34
35impl Command {
36    /// Create an APDU Command.
37    ///
38    /// `data` must be smaller than 64 kbyte. If a larger `data` is passed,
39    /// this fn will panic.
40    pub fn new(cla: u8, ins: u8, p1: u8, p2: u8, data: impl Into<Data>) -> Result<Self, Error> {
41        // This constructor is the only place `data` gets set, so it's
42        // sufficient to check it here.
43        let data = data.into();
44
45        if data.len() > u16::MAX as usize {
46            Err(Error::InternalError(
47                "Command data too large, must be <64 kbyte".to_string(),
48            ))
49        } else {
50            Ok(Command {
51                cla,
52                ins,
53                p1,
54                p2,
55                data,
56            })
57        }
58    }
59
60    pub(crate) fn ins(&self) -> u8 {
61        self.ins
62    }
63
64    pub(crate) fn p1(&self) -> u8 {
65        self.p1
66    }
67
68    pub(crate) fn p2(&self) -> u8 {
69        self.p2
70    }
71
72    pub(crate) fn data(&self) -> &Data {
73        &self.data
74    }
75
76    /// Serialize a Command (for sending to a card).
77    ///
78    /// See OpenPGP card spec, chapter 7 (pg 47)
79    pub(crate) fn serialize(
80        &self,
81        ext_len: bool,
82        expect_response: Expect,
83    ) -> Result<Vec<u8>, Error> {
84        // FIXME? (from scd/apdu.c):
85        //  T=0 does not allow the use of Lc together with Le;
86        //  thus disable Le in this case.
87
88        // "number of bytes in the command data field"
89        debug_assert!(self.data.len() <= u16::MAX as usize);
90
91        let nc = self.data.len() as u16;
92
93        let mut buf = vec![self.cla, self.ins, self.p1, self.p2];
94        buf.extend(Self::make_lc(nc, ext_len)?);
95
96        match &self.data {
97            Data::Plain(data) => buf.extend(data),
98            Data::Sensitive(sensitive) => buf.extend(sensitive.expose_secret()),
99        }
100
101        buf.extend(Self::make_le(nc, ext_len, expect_response));
102
103        Ok(buf)
104    }
105
106    /// Encode len for Lc field
107    fn make_lc(len: u16, ext_len: bool) -> Result<Vec<u8>, crate::Error> {
108        if !ext_len && len > 0xff {
109            return Err(crate::Error::InternalError(format!(
110                "Command len = {len:x?}, but extended length is unsupported by backend"
111            )));
112        }
113
114        if len == 0 {
115            Ok(vec![])
116        } else if !ext_len {
117            Ok(vec![len as u8])
118        } else {
119            Ok(vec![0, (len >> 8) as u8, (len & 255) as u8])
120        }
121    }
122
123    /// Encode value for Le field
124    /// ("maximum number of bytes expected in the response data field").
125    fn make_le(nc: u16, ext_len: bool, expect_response: Expect) -> Vec<u8> {
126        match (ext_len, expect_response) {
127            (_, Expect::Empty) => {
128                // No response data expected.
129                // "If the Le field is absent, then Ne is zero"
130                vec![]
131            }
132            (false, Expect::Some) => {
133                // A short Le field consists of one byte with any value
134                vec![0]
135            }
136            (false, Expect::Short(size)) => {
137                // A short Le field consists of one byte with any value
138                vec![size]
139            }
140            (true, Expect::Some) => {
141                if nc == 0 {
142                    // "three bytes (one byte set to '00' followed by two
143                    // bytes with any value) if the Lc field is absent"
144                    vec![0, 0, 0]
145                } else {
146                    // "two bytes (with any value) if an extended Lc field
147                    // is present"
148                    vec![0, 0]
149                }
150            }
151            _ => {
152                unreachable!("This should not happen")
153            }
154        }
155    }
156}
157
158pub(crate) enum Data {
159    Plain(Vec<u8>),
160    Sensitive(SecretVec<u8>),
161}
162
163impl std::fmt::Debug for Data {
164    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165        match self {
166            Data::Plain(vec) => write!(f, "{vec:02x?}"),
167            Data::Sensitive(_) => write!(f, "REDACTED"),
168        }
169    }
170}
171
172impl Data {
173    #[must_use]
174    fn len(&self) -> usize {
175        match self {
176            Data::Plain(vec) => vec.len(),
177            Data::Sensitive(secret_vec) => secret_vec.expose_secret().len(),
178        }
179    }
180
181    #[must_use]
182    pub fn is_empty(&self) -> bool {
183        self.len() == 0
184    }
185}
186
187impl From<Vec<u8>> for Data {
188    fn from(value: Vec<u8>) -> Self {
189        Data::Plain(value)
190    }
191}
192
193impl From<SecretVec<u8>> for Data {
194    fn from(value: SecretVec<u8>) -> Self {
195        Data::Sensitive(value)
196    }
197}