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