1#[cfg(all(feature = "wasm", feature = "multithreaded"))]
6compile_error!("wasm and multithreaded can't both be active");
7
8pub mod btc;
9pub mod cardano;
10pub mod error;
11pub mod eth;
12mod noise;
13pub mod runtime;
14#[cfg(feature = "simulator")]
15pub mod simulator;
16#[cfg(feature = "usb")]
17pub mod usb;
18#[cfg(feature = "wasm")]
19pub mod wasm;
20
21mod antiklepto;
22mod communication;
23mod constants;
24mod keypath;
25mod u2fframing;
26mod util;
27
28#[allow(clippy::all)]
30pub mod pb {
31 include!("./shiftcrypto.bitbox02.rs");
32}
33
34use crate::error::{BitBoxError, Error};
35
36use pb::request::Request;
37use pb::response::Response;
38use runtime::Runtime;
39
40use noise_protocol::DH;
41use prost::Message;
42
43use futures_util::lock::{Mutex as AsyncMutex, MutexGuard as ApiMutexGuard};
44use std::sync::Mutex;
45
46pub use keypath::Keypath;
47pub use noise::PersistedNoiseConfig;
48pub use noise::{ConfigError, NoiseConfig, NoiseConfigData, NoiseConfigNoCache};
49pub use util::Threading;
50
51use communication::HwwCommunication;
52
53pub use communication::Product;
54pub use communication::{Error as CommunicationError, ReadWrite};
55
56const OP_I_CAN_HAS_HANDSHAEK: u8 = b'h';
57const OP_HER_COMEZ_TEH_HANDSHAEK: u8 = b'H';
58const OP_I_CAN_HAS_PAIRIN_VERIFICASHUN: u8 = b'v';
59const OP_NOISE_MSG: u8 = b'n';
60const _OP_ATTESTATION: u8 = b'a';
61const OP_UNLOCK: u8 = b'u';
62
63const RESPONSE_SUCCESS: u8 = 0x00;
64
65type Cipher = noise_rust_crypto::ChaCha20Poly1305;
66type HandshakeState =
67 noise_protocol::HandshakeState<noise_rust_crypto::X25519, Cipher, noise_rust_crypto::Sha256>;
68
69type CipherState = noise_protocol::CipherState<Cipher>;
70
71pub struct BitBox<R: Runtime> {
73 communication: communication::HwwCommunication<R>,
74 noise_config: Box<dyn NoiseConfig>,
75}
76
77pub type PairingCode = String;
78
79impl<R: Runtime> BitBox<R> {
80 async fn from(
81 device: Box<dyn communication::ReadWrite>,
82 noise_config: Box<dyn NoiseConfig>,
83 ) -> Result<BitBox<R>, Error> {
84 Ok(BitBox {
85 communication: HwwCommunication::from(device).await?,
86 noise_config,
87 })
88 }
89
90 pub async fn from_transport(
99 transport: Box<dyn communication::ReadWrite>,
100 noise_config: Box<dyn NoiseConfig>,
101 ) -> Result<BitBox<R>, Error> {
102 let comm = Box::new(communication::U2fHidCommunication::from(
103 transport,
104 communication::FIRMWARE_CMD,
105 ));
106 Self::from(comm, noise_config).await
107 }
108
109 #[cfg(feature = "usb")]
115 pub async fn from_hid_device(
116 device: hidapi::HidDevice,
117 noise_config: Box<dyn NoiseConfig>,
118 ) -> Result<BitBox<R>, Error> {
119 Self::from_transport(Box::new(crate::usb::HidDevice::new(device)), noise_config).await
120 }
121
122 #[cfg(feature = "simulator")]
123 pub async fn from_simulator(
124 endpoint: Option<&str>,
125 noise_config: Box<dyn NoiseConfig>,
126 ) -> Result<BitBox<R>, Error> {
127 Self::from_transport(
128 crate::simulator::try_connect::<R>(endpoint).await?,
129 noise_config,
130 )
131 .await
132 }
133
134 pub async fn unlock_and_pair(self) -> Result<PairingBitBox<R>, Error> {
136 self.communication
137 .query(&[OP_UNLOCK])
138 .await
139 .or(Err(Error::Unknown))?;
140 self.pair().await
141 }
142
143 async fn handshake_query(&self, msg: &[u8]) -> Result<Vec<u8>, Error> {
144 let mut framed_msg = vec![OP_HER_COMEZ_TEH_HANDSHAEK];
145 framed_msg.extend_from_slice(msg);
146 let mut response = self.communication.query(&framed_msg).await?;
147 if response.is_empty() || response[0] != RESPONSE_SUCCESS {
148 return Err(Error::Noise);
149 }
150 Ok(response.split_off(1))
151 }
152
153 async fn pair(self) -> Result<PairingBitBox<R>, Error> {
154 let mut config_data = self.noise_config.read_config()?;
155 let host_static_key = match config_data.get_app_static_privkey() {
156 Some(k) => noise_rust_crypto::sensitive::Sensitive::from(k),
157 None => {
158 let k = noise_rust_crypto::X25519::genkey();
159 config_data.set_app_static_privkey(&k[..])?;
160 self.noise_config.store_config(&config_data)?;
161 k
162 }
163 };
164 let mut host = HandshakeState::new(
165 noise_protocol::patterns::noise_xx(),
166 true,
167 b"Noise_XX_25519_ChaChaPoly_SHA256",
168 Some(host_static_key),
169 None,
170 None,
171 None,
172 );
173
174 if self
175 .communication
176 .query(&[OP_I_CAN_HAS_HANDSHAEK])
177 .await?
178 .as_slice()
179 != [RESPONSE_SUCCESS]
180 {
181 return Err(Error::Noise);
182 }
183
184 let host_handshake_1 = host.write_message_vec(b"").or(Err(Error::Noise))?;
185 let bb02_handshake_1 = self.handshake_query(&host_handshake_1).await?;
186
187 host.read_message_vec(&bb02_handshake_1)
188 .or(Err(Error::Noise))?;
189 let host_handshake_2 = host.write_message_vec(b"").or(Err(Error::Noise))?;
190
191 let bb02_handshake_2 = self.handshake_query(&host_handshake_2).await?;
192 let remote_static_pubkey = host.get_rs().ok_or(Error::Noise)?;
193 let pairing_verfication_required_by_app = !self
194 .noise_config
195 .read_config()?
196 .contains_device_static_pubkey(&remote_static_pubkey);
197 let pairing_verification_required_by_device = bb02_handshake_2.as_slice() == [0x01];
198 if pairing_verfication_required_by_app || pairing_verification_required_by_device {
199 let format_hash = |h| {
200 let encoded = base32::encode(base32::Alphabet::Rfc4648 { padding: true }, h);
201 format!(
202 "{} {}\n{} {}",
203 &encoded[0..5],
204 &encoded[5..10],
205 &encoded[10..15],
206 &encoded[15..20]
207 )
208 };
209 let handshake_hash: [u8; 32] = host.get_hash().try_into().or(Err(Error::Noise))?;
210 let pairing_code = format_hash(&handshake_hash);
211
212 Ok(PairingBitBox::from(
213 self.communication,
214 host,
215 self.noise_config,
216 Some(pairing_code),
217 ))
218 } else {
219 Ok(PairingBitBox::from(
220 self.communication,
221 host,
222 self.noise_config,
223 None,
224 ))
225 }
226 }
227}
228
229pub struct PairingBitBox<R: Runtime> {
232 communication: communication::HwwCommunication<R>,
233 host: HandshakeState,
234 noise_config: Box<dyn NoiseConfig>,
235 pairing_code: Option<String>,
236}
237
238impl<R: Runtime> PairingBitBox<R> {
239 fn from(
240 communication: communication::HwwCommunication<R>,
241 host: HandshakeState,
242 noise_config: Box<dyn NoiseConfig>,
243 pairing_code: Option<String>,
244 ) -> Self {
245 PairingBitBox {
246 communication,
247 host,
248 noise_config,
249 pairing_code,
250 }
251 }
252
253 pub fn get_pairing_code(&self) -> Option<String> {
261 self.pairing_code.clone()
262 }
263
264 pub async fn wait_confirm(self) -> Result<PairedBitBox<R>, Error> {
266 if self.pairing_code.is_some() {
267 let response = self
268 .communication
269 .query(&[OP_I_CAN_HAS_PAIRIN_VERIFICASHUN])
270 .await?;
271 if response.as_slice() != [RESPONSE_SUCCESS] {
272 return Err(Error::NoisePairingRejected);
273 }
274
275 let remote_static_pubkey = self.host.get_rs().ok_or(Error::Noise)?;
276 let mut config_data = self.noise_config.read_config()?;
277 config_data.add_device_static_pubkey(&remote_static_pubkey);
278 self.noise_config.store_config(&config_data)?;
279 }
280 Ok(PairedBitBox::from(self.communication, self.host))
281 }
282}
283
284pub struct PairedBitBox<R: Runtime> {
287 communication: communication::HwwCommunication<R>,
288 api_mutex: AsyncMutex<()>,
289 noise_send: Mutex<CipherState>,
290 noise_recv: Mutex<CipherState>,
291}
292
293impl<R: Runtime> PairedBitBox<R> {
294 fn from(communication: communication::HwwCommunication<R>, host: HandshakeState) -> Self {
295 let (send, recv) = host.get_ciphers();
296 PairedBitBox {
297 communication,
298 api_mutex: AsyncMutex::new(()),
299 noise_send: Mutex::new(send),
300 noise_recv: Mutex::new(recv),
301 }
302 }
303
304 pub(crate) async fn begin_api_call(&self) -> ApiMutexGuard<'_, ()> {
311 self.api_mutex.lock().await
312 }
313
314 fn validate_version(&self, comparison: &'static str) -> Result<(), Error> {
315 if semver::VersionReq::parse(comparison)
316 .or(Err(Error::Unknown))?
317 .matches(&self.communication.info.version)
318 {
319 Ok(())
320 } else {
321 Err(Error::Version(comparison))
322 }
323 }
324
325 async fn query_proto(&self, request: Request) -> Result<Response, Error> {
326 let mut encrypted = vec![OP_NOISE_MSG];
327 encrypted.extend_from_slice({
328 let mut send = self.noise_send.lock().unwrap();
329 let proto_msg = pb::Request {
330 request: Some(request),
331 };
332 &send.encrypt_vec(&proto_msg.encode_to_vec())
333 });
334
335 let response = self.communication.query(&encrypted).await?;
336 if response.is_empty() || response[0] != RESPONSE_SUCCESS {
337 return Err(Error::UnexpectedResponse);
338 }
339 let decrypted = {
340 let mut recv = self.noise_recv.lock().unwrap();
341 recv.decrypt_vec(&response[1..]).or(Err(Error::Noise))?
342 };
343 match pb::Response::decode(&decrypted[..]) {
344 Ok(pb::Response {
345 response: Some(Response::Error(pb::Error { code, .. })),
346 }) => match code {
347 101 => Err(BitBoxError::InvalidInput.into()),
348 102 => Err(BitBoxError::Memory.into()),
349 103 => Err(BitBoxError::Generic.into()),
350 104 => Err(BitBoxError::UserAbort.into()),
351 105 => Err(BitBoxError::InvalidState.into()),
352 106 => Err(BitBoxError::Disabled.into()),
353 107 => Err(BitBoxError::Duplicate.into()),
354 108 => Err(BitBoxError::NoiseEncrypt.into()),
355 109 => Err(BitBoxError::NoiseDecrypt.into()),
356 _ => Err(BitBoxError::Unknown.into()),
357 },
358 Ok(pb::Response {
359 response: Some(response),
360 }) => Ok(response),
361 _ => Err(Error::ProtobufDecode),
362 }
363 }
364
365 pub async fn device_info(&self) -> Result<pb::DeviceInfoResponse, Error> {
366 let _api_call = self.begin_api_call().await;
367 match self
368 .query_proto(Request::DeviceInfo(pb::DeviceInfoRequest {}))
369 .await?
370 {
371 Response::DeviceInfo(di) => Ok(di),
372 _ => Err(Error::UnexpectedResponse),
373 }
374 }
375
376 pub fn product(&self) -> Product {
378 self.communication.info.product
379 }
380
381 fn is_multi_edition(&self) -> bool {
382 matches!(
383 self.product(),
384 crate::Product::BitBox02Multi | crate::Product::BitBox02NovaMulti
385 )
386 }
387
388 pub fn version(&self) -> &semver::Version {
390 &self.communication.info.version
391 }
392
393 pub async fn root_fingerprint(&self) -> Result<String, Error> {
395 let _api_call = self.begin_api_call().await;
396 self.root_fingerprint_inner().await.map(hex::encode)
397 }
398
399 pub(crate) async fn root_fingerprint_inner(&self) -> Result<Vec<u8>, Error> {
400 match self
401 .query_proto(Request::Fingerprint(pb::RootFingerprintRequest {}))
402 .await?
403 {
404 Response::Fingerprint(pb::RootFingerprintResponse { fingerprint }) => Ok(fingerprint),
405 _ => Err(Error::UnexpectedResponse),
406 }
407 }
408
409 pub async fn show_mnemonic(&self) -> Result<(), Error> {
411 let _api_call = self.begin_api_call().await;
412 match self
413 .query_proto(Request::ShowMnemonic(pb::ShowMnemonicRequest {}))
414 .await?
415 {
416 Response::Success(_) => Ok(()),
417 _ => Err(Error::UnexpectedResponse),
418 }
419 }
420
421 pub async fn restore_from_mnemonic(&self) -> Result<(), Error> {
423 let _api_call = self.begin_api_call().await;
424 let now = std::time::SystemTime::now();
425 let duration_since_epoch = now.duration_since(std::time::UNIX_EPOCH).unwrap();
426 match self
427 .query_proto(Request::RestoreFromMnemonic(
428 pb::RestoreFromMnemonicRequest {
429 timestamp: duration_since_epoch.as_secs() as u32,
430 timezone_offset: chrono::Local::now().offset().local_minus_utc(),
431 },
432 ))
433 .await?
434 {
435 Response::Success(_) => Ok(()),
436 _ => Err(Error::UnexpectedResponse),
437 }
438 }
439
440 pub async fn change_password(&self) -> Result<(), Error> {
443 let _api_call = self.begin_api_call().await;
444 self.validate_version(">=9.25.0")?;
445 match self
446 .query_proto(Request::ChangePassword(pb::ChangePasswordRequest {}))
447 .await?
448 {
449 Response::Success(_) => Ok(()),
450 _ => Err(Error::UnexpectedResponse),
451 }
452 }
453
454 pub async fn bip85_app_bip39(&self) -> Result<(), Error> {
457 let _api_call = self.begin_api_call().await;
458 self.validate_version(">=9.17.0")?;
459 match self
460 .query_proto(Request::Bip85(pb::Bip85Request {
461 app: Some(pb::bip85_request::App::Bip39(())),
462 }))
463 .await?
464 {
465 Response::Bip85(pb::Bip85Response {
466 app: Some(pb::bip85_response::App::Bip39(())),
467 }) => Ok(()),
468 _ => Err(Error::UnexpectedResponse),
469 }
470 }
471}
472
473#[cfg(all(test, not(feature = "multithreaded")))]
474mod tests {
475 use super::*;
476 use crate::communication::{Error as CommunicationError, ReadWrite};
477 use crate::runtime::DefaultRuntime;
478 use crate::util::Threading;
479 use async_trait::async_trait;
480 use futures_channel::oneshot;
481 use prost::Message;
482 use std::cell::RefCell;
483 use std::rc::Rc;
484
485 struct BlockingState {
486 writes: usize,
487 reads: usize,
488 first_read_started: Option<oneshot::Sender<()>>,
489 release_first_read: Option<oneshot::Receiver<()>>,
490 response_cipher: CipherState,
491 }
492
493 struct BlockingReadWrite {
494 state: Rc<RefCell<BlockingState>>,
495 }
496
497 impl Threading for BlockingReadWrite {}
498
499 #[async_trait(?Send)]
500 impl ReadWrite for BlockingReadWrite {
501 fn write(&self, msg: &[u8]) -> Result<usize, CommunicationError> {
502 self.state.borrow_mut().writes += 1;
503 Ok(msg.len())
504 }
505
506 async fn read(&self) -> Result<Vec<u8>, CommunicationError> {
507 let (read_index, started, release) = {
508 let mut state = self.state.borrow_mut();
509 let read_index = state.reads;
510 state.reads += 1;
511 (
512 read_index,
513 state.first_read_started.take(),
514 state.release_first_read.take(),
515 )
516 };
517 if read_index == 0 {
518 if let Some(started) = started {
519 let _ = started.send(());
520 }
521 if let Some(release) = release {
522 let _ = release.await;
523 }
524 }
525
526 let response = pb::Response {
527 response: Some(Response::DeviceInfo(pb::DeviceInfoResponse {
528 name: "BitBox".into(),
529 initialized: true,
530 version: "9.26.0".into(),
531 mnemonic_passphrase_enabled: false,
532 monotonic_increments_remaining: 42,
533 securechip_model: "ATECC608B".into(),
534 bluetooth: None,
535 password_stretching_algo: "pwhash".into(),
536 })),
537 }
538 .encode_to_vec();
539 let encrypted = self
540 .state
541 .borrow_mut()
542 .response_cipher
543 .encrypt_vec(&response);
544 let mut framed = vec![0x00, RESPONSE_SUCCESS];
545 framed.extend_from_slice(&encrypted);
546 Ok(framed)
547 }
548 }
549
550 fn paired_for_test(state: Rc<RefCell<BlockingState>>) -> PairedBitBox<DefaultRuntime> {
551 let key = [7u8; 32];
552 PairedBitBox {
553 communication: communication::HwwCommunication::from_test_parts(
554 Box::new(BlockingReadWrite { state }),
555 communication::Info {
556 version: semver::Version::parse("9.26.0").unwrap(),
557 product: Product::BitBox02Multi,
558 unlocked: true,
559 initialized: Some(true),
560 },
561 ),
562 api_mutex: AsyncMutex::new(()),
563 noise_send: Mutex::new(CipherState::new(&key, 0)),
564 noise_recv: Mutex::new(CipherState::new(&key, 0)),
565 }
566 }
567
568 #[tokio::test]
569 async fn paired_api_calls_are_serialized() {
570 let (started_tx, started_rx) = oneshot::channel();
571 let (release_tx, release_rx) = oneshot::channel();
572 let state = Rc::new(RefCell::new(BlockingState {
573 writes: 0,
574 reads: 0,
575 first_read_started: Some(started_tx),
576 release_first_read: Some(release_rx),
577 response_cipher: CipherState::new(&[7u8; 32], 0),
578 }));
579 let paired = Rc::new(paired_for_test(Rc::clone(&state)));
580
581 let local = tokio::task::LocalSet::new();
582 local
583 .run_until(async move {
584 let first = tokio::task::spawn_local({
585 let paired = Rc::clone(&paired);
586 async move { paired.device_info().await }
587 });
588 started_rx.await.unwrap();
589 assert_eq!(state.borrow().writes, 1);
590
591 let second = tokio::task::spawn_local({
592 let paired = Rc::clone(&paired);
593 async move { paired.device_info().await }
594 });
595 tokio::task::yield_now().await;
596 assert_eq!(state.borrow().writes, 1);
597
598 release_tx.send(()).unwrap();
599 first.await.unwrap().unwrap();
600 second.await.unwrap().unwrap();
601 assert_eq!(state.borrow().writes, 2);
602 })
603 .await;
604 }
605}