1mod apdu;
13mod command;
14mod yubikey;
15mod yubikey4;
16
17use std::collections::HashSet;
18
19use card_backend::{CardBackend, CardTransaction, SmartcardError};
20
21use crate::apdu::{Command, Expect, RawResponse, Response};
22use crate::yubikey::{applications_to_bitmask, AID_MGR, AID_OTP, TAG_NFC_ENABLED, TAG_USB_ENABLED};
23pub use crate::yubikey::{Application, DeviceInfo, FormFactor, Version};
24
25const BUF_SIZE: usize = 256;
26
27enum Interface {
28 Usb,
29 Nfc,
30}
31
32pub struct YkManagement {
34 card: Box<dyn CardBackend + Sync + Send>,
35 version: Version,
36}
37
38impl YkManagement {
39 fn send(
40 tx: &mut (dyn CardTransaction + Sync + Send),
41 cmd: Command,
42 response: bool,
43 ) -> Result<Response, SmartcardError> {
44 let exp = match response {
45 true => Expect::Some,
46 false => Expect::Empty,
47 };
48
49 let send = cmd.serialize(false, exp)?;
50
51 log::trace!("send: {:x?}", send);
52
53 let res = tx
54 .transmit(&send, BUF_SIZE)
55 .map_err(|e| SmartcardError::Error(format!("{e:?}")))?;
56
57 log::trace!("received: {:x?}", res);
58
59 let raw = RawResponse::try_from(res)?;
60 raw.try_into()
61 }
62
63 pub fn select(mut card: Box<dyn CardBackend + Sync + Send>) -> Result<Self, SmartcardError> {
69 let version = {
70 let mut tx = card.transaction(None)?;
71
72 let cmd = command::select(AID_MGR.into());
73 let resp = YkManagement::send(&mut *tx, cmd, true)?;
74
75 let mut data = resp.as_ref();
76
77 if data[data.len() - 2..] == [0x90, 0x00] {
79 data = &data[0..data.len() - 2];
81 }
82
83 let s = String::from_utf8_lossy(data);
84 let version = Version::try_from(s.to_string())?;
85
86 if version.major() == 3 {
88 let _resp = YkManagement::send(
90 &mut *tx,
91 Command::new(0xa4, 0x04, 0x00, 0x08, vec![]),
92 false,
93 );
94 let cmd = command::select(AID_OTP.into());
102 let _resp = YkManagement::send(&mut *tx, cmd, true)?;
103
104 }
106
107 version
108 };
109
110 Ok(Self { card, version })
111 }
112
113 pub fn version(&self) -> Version {
115 self.version
116 }
117
118 pub fn read_config(&mut self) -> Result<DeviceInfo, SmartcardError> {
122 log::debug!("read_config");
123
124 let data = YkManagement::send(
129 &mut *self.card.transaction(None)?,
130 command::read_config(),
131 true,
132 )
133 .map(move |r| r.as_ref().to_vec())?;
134
135 DeviceInfo::try_from(data.as_slice())
136 }
137
138 pub(crate) fn set_mode(&mut self, apps: HashSet<Application>) -> Result<(), SmartcardError> {
140 log::debug!("set_mode");
141
142 if self.version.major() != 4 {
143 unimplemented!();
144 }
145
146 let interface = yubikey4::usb_applications_to_interface(apps);
147 log::trace!(" interface: {:?}", interface);
148
149 let mode = yubikey4::interfaces_to_mode(interface);
150 log::trace!(" mode: {}", mode);
151
152 let cmd = command::set_mode_device_config(vec![mode, 0x00, 0x00, 0x00]);
153
154 YkManagement::send(&mut *self.card.transaction(None)?, cmd, false)?;
155
156 Ok(())
157 }
158
159 pub(crate) fn write_config(
161 &mut self,
162 apps: HashSet<Application>,
163 iface: Interface,
164 ) -> Result<(), SmartcardError> {
165 log::debug!("write_config_usb");
166
167 if self.version.major() != 5 {
168 unimplemented!();
169 }
170
171 let bitmask: u16 = applications_to_bitmask(apps);
172 let bitmask = bitmask.to_be_bytes();
173
174 let cmd = command::write_config(vec![
175 0x04, match iface {
177 Interface::Usb => TAG_USB_ENABLED,
178 Interface::Nfc => TAG_NFC_ENABLED,
179 },
180 0x02, bitmask[0],
182 bitmask[1],
183 ]);
184
185 YkManagement::send(&mut *self.card.transaction(None)?, cmd, false)?;
186
187 Ok(())
188 }
189
190 fn change_application_set(apps: &mut HashSet<Application>, change: &[Application], on: bool) {
191 if on {
192 change.iter().for_each(|x| {
194 apps.insert(*x);
195 });
196 } else {
197 change.iter().for_each(|x| {
199 apps.remove(x);
200 });
201 }
202 }
203
204 pub fn applications_change_usb(
210 &mut self,
211 change: &[Application],
212 on: bool,
213 ) -> Result<(), SmartcardError> {
214 log::debug!("applications_change_usb, change: {:?}, on: {}", change, on);
215
216 let cfg = self.read_config()?;
217
218 let mut apps = cfg.usb_enabled();
219 Self::change_application_set(&mut apps, change, on);
220
221 match self.version.major() {
222 5 => self.write_config(apps, Interface::Usb)?,
223 4 => self.set_mode(apps)?,
224 _ => unimplemented!(),
225 };
226
227 Ok(())
228 }
229
230 pub fn applications_change_nfc(
236 &mut self,
237 change: &[Application],
238 on: bool,
239 ) -> Result<(), SmartcardError> {
240 log::debug!("applications_change_nfc, change: {:?}, on: {}", change, on);
241
242 let cfg = self.read_config()?;
243
244 let mut apps = cfg.nfc_enabled();
245 Self::change_application_set(&mut apps, change, on);
246
247 match self.version.major() {
248 5 => self.write_config(apps, Interface::Nfc)?,
249 _ => unimplemented!(),
250 };
251
252 Ok(())
253 }
254}