bitbox_api/
communication.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use 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
128// sinve v7.0.0, requets and responses are framed with hww* codes.
129// hwwReq* are HWW-level framing opcodes of requests.
130// New request.
131const HWW_REQ_NEW: u8 = 0x00;
132// Poll an outstanding request for completion.
133const HWW_REQ_RETRY: u8 = 0x01;
134// Cancel any outstanding request.
135// const HWW_REQ_CANCEL: u8 = 0x02;
136// INFO api call (used to be OP_INFO api call), graduated to the toplevel framing so it works
137// the same way for all firmware versions.
138const HWW_INFO: u8 = b'i';
139
140// hwwRsp* are HWW-level framing pocodes of responses.
141
142// Request finished, payload is valid.
143const HWW_RSP_ACK: u8 = 0x00;
144// Request is outstanding, retry later with hwwOpRetry.
145const HWW_RSP_NOTREADY: u8 = 0x01;
146// Device is busy, request was dropped. Client should retry the exact same msg.
147const HWW_RSP_BUSY: u8 = 0x02;
148// Bad request.
149const 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    // Is None before firmware version 9.20.0.
167    #[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        // communication message framing since 7.0.0
228        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}