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    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    // Is None before firmware version 9.20.0.
165    #[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        // communication message framing since 7.0.0
226        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}