1#[cfg(any(feature = "bitbox", feature = "keepkey", feature = "trezor"))]
6use anyhow::Context;
7use anyhow::Result;
8#[cfg(feature = "bitbox")]
9use bhwi::bitbox::{SetupEntropy, SetupMode};
10use bhwi::common::DeviceContext;
11#[cfg(any(feature = "bitbox", feature = "keepkey", feature = "trezor"))]
12use chrono::Local;
13use rand_core::{OsRng, RngCore};
14
15#[cfg(feature = "trezor")]
21pub fn trezor_setup_context() -> DeviceContext {
22 let mut host_entropy = [0; 32];
23 OsRng.fill_bytes(&mut host_entropy);
24 bhwi_async::management::trezor_setup_context(host_entropy)
25}
26
27#[cfg(feature = "trezor")]
35pub fn trezor_pin_context(positions: String) -> Result<DeviceContext> {
36 Ok(bhwi_async::management::trezor_pin_context_from_positions(
37 positions,
38 )?)
39}
40
41#[cfg(feature = "trezor")]
47pub fn trezor_restore_context() -> Result<DeviceContext> {
48 let u2f_counter = u2f_counter_from(Local::now().timestamp())?;
49 Ok(bhwi_async::management::trezor_restore_context(u2f_counter))
50}
51
52#[cfg(feature = "keepkey")]
58pub fn keepkey_setup_context() -> DeviceContext {
59 let mut host_entropy = [0; 32];
60 OsRng.fill_bytes(&mut host_entropy);
61 bhwi_async::management::keepkey_setup_context(host_entropy)
62}
63
64#[cfg(feature = "keepkey")]
72pub fn keepkey_pin_context(positions: String) -> Result<DeviceContext> {
73 Ok(bhwi_async::management::keepkey_pin_context_from_positions(
74 positions,
75 )?)
76}
77
78#[cfg(feature = "keepkey")]
84pub fn keepkey_restore_context() -> Result<DeviceContext> {
85 let u2f_counter = u2f_counter_from(Local::now().timestamp())?;
86 Ok(bhwi_async::management::keepkey_restore_context(u2f_counter))
87}
88
89#[cfg(any(feature = "keepkey", feature = "trezor"))]
90fn u2f_counter_from(timestamp: i64) -> Result<u32> {
91 u32::try_from(timestamp).context("current timestamp does not fit in u32")
92}
93
94#[cfg(feature = "bitbox")]
107pub fn bitbox_setup_context(is_emulated: bool) -> Result<DeviceContext> {
108 let (timestamp, timezone_offset) = timestamp_and_timezone_offset()?;
109 let mode = if is_emulated {
110 SetupMode::RestoreFromMnemonic
111 } else {
112 let mut entropy = [0; 32];
113 OsRng.fill_bytes(&mut entropy);
114 SetupMode::NewWallet {
115 entropy: SetupEntropy::new(entropy),
116 }
117 };
118 Ok(bhwi_async::management::bitbox_setup_context(
119 mode,
120 timestamp,
121 timezone_offset,
122 ))
123}
124
125#[cfg(feature = "bitbox")]
133pub fn bitbox_restore_context() -> Result<DeviceContext> {
134 let (timestamp, timezone_offset) = timestamp_and_timezone_offset()?;
135 Ok(bhwi_async::management::bitbox_restore_context(
136 timestamp,
137 timezone_offset,
138 ))
139}
140
141#[cfg(feature = "bitbox")]
142fn timestamp_and_timezone_offset() -> Result<(u32, i32)> {
143 let now = Local::now();
144 timestamp_and_timezone_offset_from(now.timestamp(), now.offset().local_minus_utc())
145}
146
147#[cfg(feature = "bitbox")]
148fn timestamp_and_timezone_offset_from(timestamp: i64, timezone_offset: i32) -> Result<(u32, i32)> {
149 let timestamp = u32::try_from(timestamp).context("current timestamp does not fit in u32")?;
150 Ok((timestamp, timezone_offset))
151}
152
153#[cfg(all(test, feature = "bitbox"))]
154mod tests {
155 use super::*;
156 use bhwi::bitbox::ManagementContext;
157
158 #[test]
159 fn simulator_setup_uses_mnemonic_restore_without_entropy() {
160 assert!(matches!(
161 bitbox_setup_context(true).unwrap(),
162 DeviceContext::BitBoxManagement(ManagementContext::Setup {
163 mode: SetupMode::RestoreFromMnemonic,
164 ..
165 })
166 ));
167 assert!(matches!(
168 bitbox_setup_context(false).unwrap(),
169 DeviceContext::BitBoxManagement(ManagementContext::Setup {
170 mode: SetupMode::NewWallet { .. },
171 ..
172 })
173 ));
174 }
175
176 #[test]
177 fn timestamp_and_timezone_offset_preserves_seconds_east_of_utc() {
178 assert_eq!(
179 timestamp_and_timezone_offset_from(1_750_000_000, 3 * 60 * 60).unwrap(),
180 (1_750_000_000, 3 * 60 * 60)
181 );
182 assert_eq!(
183 timestamp_and_timezone_offset_from(1_750_000_000, -7 * 60 * 60).unwrap(),
184 (1_750_000_000, -7 * 60 * 60)
185 );
186 }
187
188 #[test]
189 fn restore_context_contains_host_time() {
190 assert!(matches!(
191 bitbox_restore_context().unwrap(),
192 DeviceContext::BitBoxManagement(ManagementContext::Restore { timestamp, .. })
193 if timestamp > 0
194 ));
195 }
196
197 #[test]
198 #[cfg(feature = "keepkey")]
199 fn keepkey_contexts_use_keepkey_management_variants() {
200 assert!(matches!(
201 keepkey_setup_context(),
202 DeviceContext::KeepKeyManagement(bhwi::keepkey::ManagementContext::Setup { .. })
203 ));
204 assert!(matches!(
205 keepkey_restore_context().unwrap(),
206 DeviceContext::KeepKeyManagement(bhwi::keepkey::ManagementContext::Restore {
207 u2f_counter
208 }) if u2f_counter > 0
209 ));
210 assert!(matches!(
211 keepkey_pin_context("7913".to_owned()).unwrap(),
212 DeviceContext::KeepKeyManagement(bhwi::keepkey::ManagementContext::Pin(_))
213 ));
214 assert!(keepkey_pin_context("not-a-pin".to_owned()).is_err());
215 }
216}