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