1use super::u2fframing::{self, U2FFraming};
4use crate::runtime::Runtime;
5use crate::util::Threading;
6use async_trait::async_trait;
7use thiserror::Error;
8
9#[cfg(any(feature = "wasm", feature = "usb", feature = "simulator"))]
10pub const FIRMWARE_CMD: u8 = 0x80 + 0x40 + 0x01;
11
12#[derive(Error, Debug)]
13pub enum Error {
14 #[error("unknown error")]
15 Unknown,
16 #[error("write error")]
17 Write,
18 #[error("read error")]
19 Read,
20 #[error("u2f framing decoding error")]
21 U2fDecode,
22 #[error("error querying device info")]
23 Info,
24 #[error("firmware version {0} required")]
25 Version(&'static str),
26}
27
28#[cfg_attr(feature = "multithreaded", async_trait)]
29#[cfg_attr(not(feature="multithreaded"), async_trait(?Send))]
30pub trait ReadWrite: Threading {
31 fn write(&self, msg: &[u8]) -> Result<usize, Error>;
32 async fn read(&self) -> Result<Vec<u8>, Error>;
33
34 async fn query(&self, msg: &[u8]) -> Result<Vec<u8>, Error> {
35 self.write(msg)?;
36 self.read().await
37 }
38}
39
40pub struct U2fHidCommunication {
41 read_write: Box<dyn ReadWrite>,
42 u2fhid: u2fframing::U2fHid,
43}
44
45impl crate::util::Threading for U2fHidCommunication {}
46
47impl U2fHidCommunication {
48 pub fn from(read_write: Box<dyn ReadWrite>, cmd: u8) -> Self {
49 U2fHidCommunication {
50 read_write,
51 u2fhid: u2fframing::U2fHid::new(cmd),
52 }
53 }
54}
55
56#[cfg_attr(feature = "multithreaded", async_trait)]
57#[cfg_attr(not(feature="multithreaded"),async_trait(?Send))]
58impl ReadWrite for U2fHidCommunication {
59 fn write(&self, msg: &[u8]) -> Result<usize, Error> {
60 let mut buf = [0u8; u2fframing::MAX_LEN];
61 let size = self.u2fhid.encode(msg, &mut buf).unwrap();
62 for chunk in buf[..size].chunks(64) {
63 self.read_write.write(chunk)?;
64 }
65 Ok(size)
66 }
67
68 async fn read(&self) -> Result<Vec<u8>, Error> {
69 let mut readbuf = self.read_write.read().await?;
70 loop {
71 match self.u2fhid.decode(&readbuf).or(Err(Error::U2fDecode))? {
72 Some(d) => {
73 return Ok(d);
74 }
75 None => {
76 let more = self.read_write.read().await?;
77 readbuf.extend_from_slice(&more);
78 }
79 }
80 }
81 }
82}
83
84#[cfg(feature = "wasm")]
85pub struct U2fWsCommunication {
86 read_write: Box<dyn ReadWrite>,
87 u2fhid: u2fframing::U2fWs,
88}
89
90#[cfg(feature = "wasm")]
91impl Threading for U2fWsCommunication {}
92
93#[cfg(feature = "wasm")]
94impl U2fWsCommunication {
95 pub fn from(read_write: Box<dyn ReadWrite>, cmd: u8) -> Self {
96 U2fWsCommunication {
97 read_write,
98 u2fhid: u2fframing::U2fWs::new(cmd),
99 }
100 }
101}
102
103#[cfg(feature = "wasm")]
104#[async_trait(?Send)]
105impl ReadWrite for U2fWsCommunication {
106 fn write(&self, msg: &[u8]) -> Result<usize, Error> {
107 let mut buf = [0u8; u2fframing::MAX_LEN];
108 let size = self.u2fhid.encode(msg, &mut buf).unwrap();
109 self.read_write.write(&buf[..size])
110 }
111
112 async fn read(&self) -> Result<Vec<u8>, Error> {
113 let mut readbuf = self.read_write.read().await?;
114 loop {
115 match self.u2fhid.decode(&readbuf).or(Err(Error::U2fDecode))? {
116 Some(d) => {
117 return Ok(d);
118 }
119 None => {
120 let more = self.read_write.read().await?;
121 readbuf.extend_from_slice(&more);
122 }
123 }
124 }
125 }
126}
127
128const HWW_REQ_NEW: u8 = 0x00;
132const HWW_REQ_RETRY: u8 = 0x01;
134const HWW_INFO: u8 = b'i';
139
140const HWW_RSP_ACK: u8 = 0x00;
144const HWW_RSP_NOTREADY: u8 = 0x01;
146const HWW_RSP_BUSY: u8 = 0x02;
148const HWW_RSP_NACK: u8 = 0x03;
150
151#[derive(PartialEq, Debug, Copy, Clone)]
152pub enum Product {
153 Unknown,
154 BitBox02Multi,
155 BitBox02BtcOnly,
156 BitBox02NovaMulti,
157 BitBox02NovaBtcOnly,
158}
159
160#[derive(Debug)]
161pub struct Info {
162 pub version: semver::Version,
163 pub product: Product,
164 #[allow(dead_code)]
165 pub unlocked: bool,
166 #[allow(dead_code)]
168 pub initialized: Option<bool>,
169}
170
171pub struct HwwCommunication<R: Runtime> {
172 communication: Box<dyn ReadWrite>,
173 pub info: Info,
174 marker: std::marker::PhantomData<R>,
175}
176
177async fn get_info(communication: &dyn ReadWrite) -> Result<Info, Error> {
178 let response = communication.query(&[HWW_INFO]).await?;
179 let (version_str_len, response) = (
180 *response.first().ok_or(Error::Info)? as usize,
181 response.get(1..).ok_or(Error::Info)?,
182 );
183 let (version_bytes, response) = (
184 response.get(..version_str_len).ok_or(Error::Info)?,
185 response.get(version_str_len..).ok_or(Error::Info)?,
186 );
187 let version_str = std::str::from_utf8(version_bytes)
188 .or(Err(Error::Info))?
189 .strip_prefix('v')
190 .ok_or(Error::Info)?;
191
192 let version = semver::Version::parse(version_str).or(Err(Error::Info))?;
193 const PLATFORM_BITBOX02: u8 = 0x00;
194 const PLATFORM_BITBOX02_NOVA: u8 = 0x02;
195 const BITBOX02_EDITION_MULTI: u8 = 0x00;
196 const BITBOX02_EDITION_BTCONLY: u8 = 0x01;
197 let platform_byte = *response.first().ok_or(Error::Info)?;
198 let edition_byte = *response.get(1).ok_or(Error::Info)?;
199 let unlocked_byte = *response.get(2).ok_or(Error::Info)?;
200 let initialized_byte = response.get(3);
201 Ok(Info {
202 version,
203 product: match (platform_byte, edition_byte) {
204 (PLATFORM_BITBOX02, BITBOX02_EDITION_MULTI) => Product::BitBox02Multi,
205 (PLATFORM_BITBOX02, BITBOX02_EDITION_BTCONLY) => Product::BitBox02BtcOnly,
206 (PLATFORM_BITBOX02_NOVA, BITBOX02_EDITION_MULTI) => Product::BitBox02NovaMulti,
207 (PLATFORM_BITBOX02_NOVA, BITBOX02_EDITION_BTCONLY) => Product::BitBox02NovaBtcOnly,
208 _ => Product::Unknown,
209 },
210 unlocked: match unlocked_byte {
211 0x00 => false,
212 0x01 => true,
213 _ => return Err(Error::Info),
214 },
215 initialized: match initialized_byte {
216 None => None,
217 Some(0x00) => Some(false),
218 Some(0x01) => Some(true),
219 _ => return Err(Error::Info),
220 },
221 })
222}
223
224impl<R: Runtime> HwwCommunication<R> {
225 pub async fn from(communication: Box<dyn ReadWrite>) -> Result<Self, Error> {
226 let info = get_info(communication.as_ref()).await?;
227 if !semver::VersionReq::parse(">=7.0.0")
229 .or(Err(Error::Unknown))?
230 .matches(&info.version)
231 {
232 return Err(Error::Version(">=7.0.0"));
233 }
234
235 Ok(HwwCommunication {
236 communication,
237 info,
238 marker: std::marker::PhantomData,
239 })
240 }
241
242 pub async fn query(&self, msg: &[u8]) -> Result<Vec<u8>, Error> {
243 let mut framed_msg = Vec::from([HWW_REQ_NEW]);
244 framed_msg.extend_from_slice(msg);
245
246 let mut response = loop {
247 let response = self.communication.query(&framed_msg).await?;
248 if let Some(&HWW_RSP_BUSY) = response.first() {
249 R::sleep(std::time::Duration::from_millis(1000)).await;
250 continue;
251 }
252 break response;
253 };
254 loop {
255 match response.first() {
256 Some(&HWW_RSP_ACK) => {
257 return Ok(response.split_off(1));
258 }
259 Some(&HWW_RSP_BUSY) => {
260 return Err(Error::Info);
261 }
262 Some(&HWW_RSP_NACK) => {
263 return Err(Error::Info);
264 }
265 Some(&HWW_RSP_NOTREADY) => {
266 R::sleep(std::time::Duration::from_millis(200)).await;
267 response = self.communication.query(&[HWW_REQ_RETRY]).await?;
268 }
269 _ => return Err(Error::Info),
270 }
271 }
272 }
273}