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