bitbox_api/
communication.rs

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
126// sinve v7.0.0, requets and responses are framed with hww* codes.
127// hwwReq* are HWW-level framing opcodes of requests.
128// New request.
129const HWW_REQ_NEW: u8 = 0x00;
130// Poll an outstanding request for completion.
131const HWW_REQ_RETRY: u8 = 0x01;
132// Cancel any outstanding request.
133// const HWW_REQ_CANCEL: u8 = 0x02;
134// INFO api call (used to be OP_INFO api call), graduated to the toplevel framing so it works
135// the same way for all firmware versions.
136const HWW_INFO: u8 = b'i';
137
138// hwwRsp* are HWW-level framing pocodes of responses.
139
140// Request finished, payload is valid.
141const HWW_RSP_ACK: u8 = 0x00;
142// Request is outstanding, retry later with hwwOpRetry.
143const HWW_RSP_NOTREADY: u8 = 0x01;
144// Device is busy, request was dropped. Client should retry the exact same msg.
145const HWW_RSP_BUSY: u8 = 0x02;
146// Bad request.
147const HWW_RSP_NACK: u8 = 0x03;
148
149#[derive(PartialEq, Debug, Copy, Clone)]
150pub enum Product {
151    Unknown,
152    BitBox02Multi,
153    BitBox02BtcOnly,
154}
155
156#[derive(Debug)]
157pub struct Info {
158    pub version: semver::Version,
159    pub product: Product,
160    #[allow(dead_code)]
161    pub unlocked: bool,
162    // Is None before firmware version 9.20.0.
163    #[allow(dead_code)]
164    pub initialized: Option<bool>,
165}
166
167pub struct HwwCommunication<R: Runtime> {
168    communication: Box<dyn ReadWrite>,
169    pub info: Info,
170    marker: std::marker::PhantomData<R>,
171}
172
173async fn get_info(communication: &dyn ReadWrite) -> Result<Info, Error> {
174    let response = communication.query(&[HWW_INFO]).await?;
175    let (version_str_len, response) = (
176        *response.first().ok_or(Error::Info)? as usize,
177        response.get(1..).ok_or(Error::Info)?,
178    );
179    let (version_bytes, response) = (
180        response.get(..version_str_len).ok_or(Error::Info)?,
181        response.get(version_str_len..).ok_or(Error::Info)?,
182    );
183    let version_str = std::str::from_utf8(version_bytes)
184        .or(Err(Error::Info))?
185        .strip_prefix('v')
186        .ok_or(Error::Info)?;
187
188    let version = semver::Version::parse(version_str).or(Err(Error::Info))?;
189    const PLATFORM_BITBOX02: u8 = 0x00;
190    const BITBOX02_EDITION_MULTI: u8 = 0x00;
191    const BITBOX02_EDITION_BTCONLY: u8 = 0x01;
192    let platform_byte = *response.first().ok_or(Error::Info)?;
193    let edition_byte = *response.get(1).ok_or(Error::Info)?;
194    let unlocked_byte = *response.get(2).ok_or(Error::Info)?;
195    let initialized_byte = response.get(3);
196    Ok(Info {
197        version,
198        product: match (platform_byte, edition_byte) {
199            (PLATFORM_BITBOX02, BITBOX02_EDITION_MULTI) => Product::BitBox02Multi,
200            (PLATFORM_BITBOX02, BITBOX02_EDITION_BTCONLY) => Product::BitBox02BtcOnly,
201            _ => Product::Unknown,
202        },
203        unlocked: match unlocked_byte {
204            0x00 => false,
205            0x01 => true,
206            _ => return Err(Error::Info),
207        },
208        initialized: match initialized_byte {
209            None => None,
210            Some(0x00) => Some(false),
211            Some(0x01) => Some(true),
212            _ => return Err(Error::Info),
213        },
214    })
215}
216
217impl<R: Runtime> HwwCommunication<R> {
218    pub async fn from(communication: Box<dyn ReadWrite>) -> Result<Self, Error> {
219        let info = get_info(communication.as_ref()).await?;
220        // communication message framing since 7.0.0
221        if !semver::VersionReq::parse(">=7.0.0")
222            .or(Err(Error::Unknown))?
223            .matches(&info.version)
224        {
225            return Err(Error::Version(">=7.0.0"));
226        }
227
228        Ok(HwwCommunication {
229            communication,
230            info,
231            marker: std::marker::PhantomData,
232        })
233    }
234
235    pub async fn query(&self, msg: &[u8]) -> Result<Vec<u8>, Error> {
236        let mut framed_msg = Vec::from([HWW_REQ_NEW]);
237        framed_msg.extend_from_slice(msg);
238
239        let mut response = loop {
240            let response = self.communication.query(&framed_msg).await?;
241            if let Some(&HWW_RSP_BUSY) = response.first() {
242                R::sleep(std::time::Duration::from_millis(1000)).await;
243                continue;
244            }
245            break response;
246        };
247        loop {
248            match response.first() {
249                Some(&HWW_RSP_ACK) => {
250                    return Ok(response.split_off(1));
251                }
252                Some(&HWW_RSP_BUSY) => {
253                    return Err(Error::Info);
254                }
255                Some(&HWW_RSP_NACK) => {
256                    return Err(Error::Info);
257                }
258                Some(&HWW_RSP_NOTREADY) => {
259                    R::sleep(std::time::Duration::from_millis(200)).await;
260                    response = self.communication.query(&[HWW_REQ_RETRY]).await?;
261                }
262                _ => return Err(Error::Info),
263            }
264        }
265    }
266}