1#[cfg(all(feature = "wasm", feature = "multithreaded"))]
4compile_error!("wasm and multithreaded can't both be active");
5
6pub mod btc;
7pub mod cardano;
8pub mod error;
9pub mod eth;
10mod noise;
11pub mod runtime;
12#[cfg(feature = "simulator")]
13pub mod simulator;
14#[cfg(feature = "usb")]
15pub mod usb;
16#[cfg(feature = "wasm")]
17pub mod wasm;
18
19mod antiklepto;
20mod communication;
21mod constants;
22mod keypath;
23mod u2fframing;
24mod util;
25
26#[allow(clippy::all)]
28pub mod pb {
29 include!("./shiftcrypto.bitbox02.rs");
30}
31
32use crate::error::{BitBoxError, Error};
33
34use pb::request::Request;
35use pb::response::Response;
36use runtime::Runtime;
37
38use noise_protocol::DH;
39use prost::Message;
40
41use std::sync::Mutex;
42
43pub use keypath::Keypath;
44pub use noise::PersistedNoiseConfig;
45pub use noise::{ConfigError, NoiseConfig, NoiseConfigData, NoiseConfigNoCache};
46pub use util::Threading;
47
48use communication::HwwCommunication;
49
50pub use communication::Product;
51
52const OP_I_CAN_HAS_HANDSHAEK: u8 = b'h';
53const OP_HER_COMEZ_TEH_HANDSHAEK: u8 = b'H';
54const OP_I_CAN_HAS_PAIRIN_VERIFICASHUN: u8 = b'v';
55const OP_NOISE_MSG: u8 = b'n';
56const _OP_ATTESTATION: u8 = b'a';
57const OP_UNLOCK: u8 = b'u';
58
59const RESPONSE_SUCCESS: u8 = 0x00;
60
61type Cipher = noise_rust_crypto::ChaCha20Poly1305;
62type HandshakeState =
63 noise_protocol::HandshakeState<noise_rust_crypto::X25519, Cipher, noise_rust_crypto::Sha256>;
64
65type CipherState = noise_protocol::CipherState<Cipher>;
66
67pub struct BitBox<R: Runtime> {
69 communication: communication::HwwCommunication<R>,
70 noise_config: Box<dyn NoiseConfig>,
71}
72
73pub type PairingCode = String;
74
75impl<R: Runtime> BitBox<R> {
76 async fn from(
77 device: Box<dyn communication::ReadWrite>,
78 noise_config: Box<dyn NoiseConfig>,
79 ) -> Result<BitBox<R>, Error> {
80 Ok(BitBox {
81 communication: HwwCommunication::from(device).await?,
82 noise_config,
83 })
84 }
85
86 #[cfg(feature = "usb")]
92 pub async fn from_hid_device(
93 device: hidapi::HidDevice,
94 noise_config: Box<dyn NoiseConfig>,
95 ) -> Result<BitBox<R>, Error> {
96 let comm = Box::new(communication::U2fHidCommunication::from(
97 Box::new(crate::usb::HidDevice::new(device)),
98 communication::FIRMWARE_CMD,
99 ));
100 Self::from(comm, noise_config).await
101 }
102
103 #[cfg(feature = "simulator")]
104 pub async fn from_simulator(
105 endpoint: Option<&str>,
106 noise_config: Box<dyn NoiseConfig>,
107 ) -> Result<BitBox<R>, Error> {
108 let comm = Box::new(communication::U2fHidCommunication::from(
109 crate::simulator::try_connect::<R>(endpoint).await?,
110 communication::FIRMWARE_CMD,
111 ));
112 Self::from(comm, noise_config).await
113 }
114
115 pub async fn unlock_and_pair(self) -> Result<PairingBitBox<R>, Error> {
117 self.communication
118 .query(&[OP_UNLOCK])
119 .await
120 .or(Err(Error::Unknown))?;
121 self.pair().await
122 }
123
124 async fn handshake_query(&self, msg: &[u8]) -> Result<Vec<u8>, Error> {
125 let mut framed_msg = vec![OP_HER_COMEZ_TEH_HANDSHAEK];
126 framed_msg.extend_from_slice(msg);
127 let mut response = self.communication.query(&framed_msg).await?;
128 if response.is_empty() || response[0] != RESPONSE_SUCCESS {
129 return Err(Error::Noise);
130 }
131 Ok(response.split_off(1))
132 }
133
134 async fn pair(self) -> Result<PairingBitBox<R>, Error> {
135 let mut config_data = self.noise_config.read_config()?;
136 let host_static_key = match config_data.get_app_static_privkey() {
137 Some(k) => noise_rust_crypto::sensitive::Sensitive::from(k),
138 None => {
139 let k = noise_rust_crypto::X25519::genkey();
140 config_data.set_app_static_privkey(&k[..])?;
141 self.noise_config.store_config(&config_data)?;
142 k
143 }
144 };
145 let mut host = HandshakeState::new(
146 noise_protocol::patterns::noise_xx(),
147 true,
148 b"Noise_XX_25519_ChaChaPoly_SHA256",
149 Some(host_static_key),
150 None,
151 None,
152 None,
153 );
154
155 if self
156 .communication
157 .query(&[OP_I_CAN_HAS_HANDSHAEK])
158 .await?
159 .as_slice()
160 != [RESPONSE_SUCCESS]
161 {
162 return Err(Error::Noise);
163 }
164
165 let host_handshake_1 = host.write_message_vec(b"").or(Err(Error::Noise))?;
166 let bb02_handshake_1 = self.handshake_query(&host_handshake_1).await?;
167
168 host.read_message_vec(&bb02_handshake_1)
169 .or(Err(Error::Noise))?;
170 let host_handshake_2 = host.write_message_vec(b"").or(Err(Error::Noise))?;
171
172 let bb02_handshake_2 = self.handshake_query(&host_handshake_2).await?;
173 let remote_static_pubkey = host.get_rs().ok_or(Error::Noise)?;
174 let pairing_verfication_required_by_app = !self
175 .noise_config
176 .read_config()?
177 .contains_device_static_pubkey(&remote_static_pubkey);
178 let pairing_verification_required_by_device = bb02_handshake_2.as_slice() == [0x01];
179 if pairing_verfication_required_by_app || pairing_verification_required_by_device {
180 let format_hash = |h| {
181 let encoded = base32::encode(base32::Alphabet::RFC4648 { padding: true }, h);
182 format!(
183 "{} {}\n{} {}",
184 &encoded[0..5],
185 &encoded[5..10],
186 &encoded[10..15],
187 &encoded[15..20]
188 )
189 };
190 let handshake_hash: [u8; 32] = host.get_hash().try_into().or(Err(Error::Noise))?;
191 let pairing_code = format_hash(&handshake_hash);
192
193 Ok(PairingBitBox::from(
194 self.communication,
195 host,
196 self.noise_config,
197 Some(pairing_code),
198 ))
199 } else {
200 Ok(PairingBitBox::from(
201 self.communication,
202 host,
203 self.noise_config,
204 None,
205 ))
206 }
207 }
208}
209
210pub struct PairingBitBox<R: Runtime> {
213 communication: communication::HwwCommunication<R>,
214 host: HandshakeState,
215 noise_config: Box<dyn NoiseConfig>,
216 pairing_code: Option<String>,
217}
218
219impl<R: Runtime> PairingBitBox<R> {
220 fn from(
221 communication: communication::HwwCommunication<R>,
222 host: HandshakeState,
223 noise_config: Box<dyn NoiseConfig>,
224 pairing_code: Option<String>,
225 ) -> Self {
226 PairingBitBox {
227 communication,
228 host,
229 noise_config,
230 pairing_code,
231 }
232 }
233
234 pub fn get_pairing_code(&self) -> Option<String> {
242 self.pairing_code.clone()
243 }
244
245 pub async fn wait_confirm(self) -> Result<PairedBitBox<R>, Error> {
247 if self.pairing_code.is_some() {
248 let response = self
249 .communication
250 .query(&[OP_I_CAN_HAS_PAIRIN_VERIFICASHUN])
251 .await?;
252 if response.as_slice() != [RESPONSE_SUCCESS] {
253 return Err(Error::NoisePairingRejected);
254 }
255
256 let remote_static_pubkey = self.host.get_rs().ok_or(Error::Noise)?;
257 let mut config_data = self.noise_config.read_config()?;
258 config_data.add_device_static_pubkey(&remote_static_pubkey);
259 self.noise_config.store_config(&config_data)?;
260 }
261 Ok(PairedBitBox::from(self.communication, self.host))
262 }
263}
264
265pub struct PairedBitBox<R: Runtime> {
268 communication: communication::HwwCommunication<R>,
269 noise_send: Mutex<CipherState>,
270 noise_recv: Mutex<CipherState>,
271}
272
273impl<R: Runtime> PairedBitBox<R> {
274 fn from(communication: communication::HwwCommunication<R>, host: HandshakeState) -> Self {
275 let (send, recv) = host.get_ciphers();
276 PairedBitBox {
277 communication,
278 noise_send: Mutex::new(send),
279 noise_recv: Mutex::new(recv),
280 }
281 }
282
283 fn validate_version(&self, comparison: &'static str) -> Result<(), Error> {
284 if semver::VersionReq::parse(comparison)
285 .or(Err(Error::Unknown))?
286 .matches(&self.communication.info.version)
287 {
288 Ok(())
289 } else {
290 Err(Error::Version(comparison))
291 }
292 }
293
294 async fn query_proto(&self, request: Request) -> Result<Response, Error> {
295 let mut encrypted = vec![OP_NOISE_MSG];
296 encrypted.extend_from_slice({
297 let mut send = self.noise_send.lock().unwrap();
298 let proto_msg = pb::Request {
299 request: Some(request),
300 };
301 &send.encrypt_vec(&proto_msg.encode_to_vec())
302 });
303
304 let response = self.communication.query(&encrypted).await?;
305 if response.is_empty() || response[0] != RESPONSE_SUCCESS {
306 return Err(Error::UnexpectedResponse);
307 }
308 let decrypted = {
309 let mut recv = self.noise_recv.lock().unwrap();
310 recv.decrypt_vec(&response[1..]).or(Err(Error::Noise))?
311 };
312 match pb::Response::decode(&decrypted[..]) {
313 Ok(pb::Response {
314 response: Some(Response::Error(pb::Error { code, .. })),
315 }) => match code {
316 101 => Err(BitBoxError::InvalidInput.into()),
317 102 => Err(BitBoxError::Memory.into()),
318 103 => Err(BitBoxError::Generic.into()),
319 104 => Err(BitBoxError::UserAbort.into()),
320 105 => Err(BitBoxError::InvalidState.into()),
321 106 => Err(BitBoxError::Disabled.into()),
322 107 => Err(BitBoxError::Duplicate.into()),
323 108 => Err(BitBoxError::NoiseEncrypt.into()),
324 109 => Err(BitBoxError::NoiseDecrypt.into()),
325 _ => Err(BitBoxError::Unknown.into()),
326 },
327 Ok(pb::Response {
328 response: Some(response),
329 }) => Ok(response),
330 _ => Err(Error::ProtobufDecode),
331 }
332 }
333
334 pub async fn device_info(&self) -> Result<pb::DeviceInfoResponse, Error> {
335 match self
336 .query_proto(Request::DeviceInfo(pb::DeviceInfoRequest {}))
337 .await?
338 {
339 Response::DeviceInfo(di) => Ok(di),
340 _ => Err(Error::UnexpectedResponse),
341 }
342 }
343
344 pub fn product(&self) -> Product {
346 self.communication.info.product
347 }
348
349 pub fn version(&self) -> &semver::Version {
351 &self.communication.info.version
352 }
353
354 pub async fn root_fingerprint(&self) -> Result<String, Error> {
356 match self
357 .query_proto(Request::Fingerprint(pb::RootFingerprintRequest {}))
358 .await?
359 {
360 Response::Fingerprint(pb::RootFingerprintResponse { fingerprint }) => {
361 Ok(hex::encode(fingerprint))
362 }
363 _ => Err(Error::UnexpectedResponse),
364 }
365 }
366
367 pub async fn show_mnemonic(&self) -> Result<(), Error> {
369 match self
370 .query_proto(Request::ShowMnemonic(pb::ShowMnemonicRequest {}))
371 .await?
372 {
373 Response::Success(_) => Ok(()),
374 _ => Err(Error::UnexpectedResponse),
375 }
376 }
377
378 pub async fn restore_from_mnemonic(&self) -> Result<(), Error> {
380 let now = std::time::SystemTime::now();
381 let duration_since_epoch = now.duration_since(std::time::UNIX_EPOCH).unwrap();
382 match self
383 .query_proto(Request::RestoreFromMnemonic(
384 pb::RestoreFromMnemonicRequest {
385 timestamp: duration_since_epoch.as_secs() as u32,
386 timezone_offset: chrono::Local::now().offset().local_minus_utc(),
387 },
388 ))
389 .await?
390 {
391 Response::Success(_) => Ok(()),
392 _ => Err(Error::UnexpectedResponse),
393 }
394 }
395
396 pub async fn bip85_app_bip39(&self) -> Result<(), Error> {
399 self.validate_version(">=9.17.0")?;
400 match self
401 .query_proto(Request::Bip85(pb::Bip85Request {
402 app: Some(pb::bip85_request::App::Bip39(())),
403 }))
404 .await?
405 {
406 Response::Bip85(pb::Bip85Response {
407 app: Some(pb::bip85_response::App::Bip39(())),
408 }) => Ok(()),
409 _ => Err(Error::UnexpectedResponse),
410 }
411 }
412}