Skip to content

Commit 21666e8

Browse files
committed
Merge remote-tracking branch 'benma/hal-sc-kdf'
2 parents f30b120 + ef85ad3 commit 21666e8

31 files changed

+737
-410
lines changed

src/rust/bitbox02-rust/src/hal.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ pub trait SecureChip {
4444
&mut self,
4545
password: &str,
4646
) -> Result<zeroize::Zeroizing<Vec<u8>>, bitbox02::securechip::Error>;
47+
fn kdf(
48+
&mut self,
49+
msg: &[u8],
50+
) -> Result<zeroize::Zeroizing<Vec<u8>>, bitbox02::securechip::Error>;
4751
}
4852

4953
/// Hardware abstraction layer for BitBox devices.
@@ -119,6 +123,13 @@ impl SecureChip for BitBox02SecureChip {
119123
) -> Result<zeroize::Zeroizing<Vec<u8>>, bitbox02::securechip::Error> {
120124
bitbox02::securechip::stretch_password(password)
121125
}
126+
127+
fn kdf(
128+
&mut self,
129+
msg: &[u8],
130+
) -> Result<zeroize::Zeroizing<Vec<u8>>, bitbox02::securechip::Error> {
131+
bitbox02::securechip::kdf(msg)
132+
}
122133
}
123134

124135
pub struct BitBox02Hal {
@@ -163,6 +174,8 @@ pub mod testing {
163174

164175
use bitcoin::hashes::{Hash, sha256};
165176

177+
use hex_lit::hex;
178+
166179
pub struct TestingRandom {
167180
mock_next_values: VecDeque<[u8; 32]>,
168181
counter: u32,
@@ -301,6 +314,23 @@ pub mod testing {
301314
hmac_result.to_byte_array().to_vec(),
302315
))
303316
}
317+
318+
fn kdf(
319+
&mut self,
320+
msg: &[u8],
321+
) -> Result<zeroize::Zeroizing<Vec<u8>>, bitbox02::securechip::Error> {
322+
self.event_counter += 1;
323+
324+
use bitcoin::hashes::{HashEngine, Hmac, HmacEngine, sha256};
325+
let mut engine = HmacEngine::<sha256::Hash>::new(&hex!(
326+
"d2e1e6b18b6c6b08433edbc1d168c1a0043774a4221877e79ed56684be5ac01b"
327+
));
328+
engine.input(msg);
329+
let hmac_result: Hmac<sha256::Hash> = Hmac::from_engine(engine);
330+
Ok(zeroize::Zeroizing::new(
331+
hmac_result.to_byte_array().to_vec(),
332+
))
333+
}
304334
}
305335

306336
pub struct TestingHal<'a> {

src/rust/bitbox02-rust/src/hww.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ mod tests {
552552
]
553553
);
554554

555-
let seed = crate::keystore::copy_seed().unwrap();
555+
let seed = crate::keystore::copy_seed(&mut mock_hal).unwrap();
556556
assert_eq!(seed.len(), host_entropy.len());
557557
mock_hal.ui = crate::workflow::testing::TestingWorkflows::new();
558558
assert!(matches!(
@@ -718,7 +718,7 @@ mod tests {
718718
);
719719

720720
// Restored seed is the same as the seed that was backed up.
721-
assert_eq!(seed, crate::keystore::copy_seed().unwrap());
721+
assert_eq!(seed, crate::keystore::copy_seed(&mut mock_hal).unwrap());
722722
}
723723
}
724724
}

src/rust/bitbox02-rust/src/hww/api.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ async fn process_api(hal: &mut impl crate::hal::Hal, request: &Request) -> Resul
184184
Request::RestoreBackup(request) => restore::from_file(hal, request).await,
185185
Request::ShowMnemonic(_) => show_mnemonic::process(hal).await,
186186
Request::RestoreFromMnemonic(request) => restore::from_mnemonic(hal, request).await,
187-
Request::ElectrumEncryptionKey(request) => electrum::process(request).await,
187+
Request::ElectrumEncryptionKey(request) => electrum::process(hal, request).await,
188188

189189
#[cfg(feature = "app-ethereum")]
190190
Request::Eth(pb::EthRequest {

src/rust/bitbox02-rust/src/hww/api/backup.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub async fn check(
3030
return Err(Error::InvalidInput);
3131
}
3232

33-
let seed = crate::keystore::copy_seed()?;
33+
let seed = crate::keystore::copy_seed(hal)?;
3434
let id = backup::id(&seed);
3535
let (backup_data, metadata) = backup::load(hal, &id).await?;
3636
if seed.as_slice() != backup_data.get_seed() {
@@ -102,7 +102,7 @@ pub async fn create(
102102
let seed = if is_initialized {
103103
unlock::unlock_keystore(hal, "Unlock device", unlock::CanCancel::Yes).await?
104104
} else {
105-
let seed = crate::keystore::copy_seed()?;
105+
let seed = crate::keystore::copy_seed(hal)?;
106106
// Yield now to give executor a chance to process USB/BLE communication, as copy_seed() causes
107107
// some delay.
108108
futures_lite::future::yield_now().await;

src/rust/bitbox02-rust/src/hww/api/bip85.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ async fn process_bip39(hal: &mut impl crate::hal::Hal) -> Result<(), Error> {
121121
})
122122
.await?;
123123

124-
let mnemonic = keystore::bip85_bip39(num_words, index)?;
124+
let mnemonic = keystore::bip85_bip39(hal, num_words, index)?;
125125
let words: Vec<&str> = mnemonic.split(' ').collect();
126126
hal.ui().show_and_confirm_mnemonic(&words).await?;
127127

@@ -151,7 +151,7 @@ async fn process_ln(
151151
})
152152
.await?;
153153

154-
Ok(keystore::bip85_ln(account_number)
154+
Ok(keystore::bip85_ln(hal, account_number)
155155
.map_err(|_| Error::Generic)?
156156
.to_vec())
157157
}

src/rust/bitbox02-rust/src/hww/api/bitcoin.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ async fn xpub(
126126
})
127127
.await?
128128
}
129-
let xpub = keystore::get_xpub_twice(keypath)
129+
let xpub = keystore::get_xpub_twice(hal, keypath)
130130
.or(Err(Error::InvalidInput))?
131131
.serialize_str(xpub_type)?;
132132
if display {
@@ -150,6 +150,7 @@ async fn xpub(
150150
}
151151

152152
pub fn derive_address_simple(
153+
hal: &mut impl crate::hal::Hal,
153154
coin: BtcCoin,
154155
simple_type: SimpleType,
155156
keypath: &[u32],
@@ -164,6 +165,7 @@ pub fn derive_address_simple(
164165
)
165166
.or(Err(Error::InvalidInput))?;
166167
Ok(common::Payload::from_simple(
168+
hal,
167169
&mut crate::xpubcache::XpubCache::new(crate::xpubcache::Compute::Twice),
168170
coin_params,
169171
simple_type,
@@ -180,7 +182,7 @@ async fn address_simple(
180182
keypath: &[u32],
181183
display: bool,
182184
) -> Result<Response, Error> {
183-
let address = derive_address_simple(coin, simple_type, keypath)?;
185+
let address = derive_address_simple(hal, coin, simple_type, keypath)?;
184186
if display {
185187
let confirm_params = confirm::Params {
186188
title: params::get(coin).name,
@@ -205,7 +207,7 @@ pub async fn address_multisig(
205207
keypath::validate_address_policy(keypath, keypath::ReceiveSpend::Receive)
206208
.or(Err(Error::InvalidInput))?;
207209
let account_keypath = &keypath[..keypath.len() - 2];
208-
multisig::validate(multisig, account_keypath)?;
210+
multisig::validate(hal, multisig, account_keypath)?;
209211
let name = match multisig::get_name(coin, multisig, account_keypath)? {
210212
Some(name) => name,
211213
None => return Err(Error::InvalidInput),
@@ -247,7 +249,7 @@ async fn address_policy(
247249
keypath::validate_address_policy(keypath, keypath::ReceiveSpend::Receive)
248250
.or(Err(Error::InvalidInput))?;
249251

250-
let parsed = policies::parse(policy, coin)?;
252+
let parsed = policies::parse(hal, policy, coin)?;
251253

252254
let name = parsed.name(coin_params)?.ok_or(Error::InvalidInput)?;
253255

@@ -316,7 +318,7 @@ pub async fn process_api(
316318
registration::process_register_script_config(hal, request).await
317319
}
318320
Request::SignMessage(request) => signmsg::process(hal, request).await,
319-
Request::Xpubs(request) => xpubs::process_xpubs(request).await,
321+
Request::Xpubs(request) => xpubs::process_xpubs(hal, request).await,
320322
// These are streamed asynchronously using the `next_request()` primitive in
321323
// bitcoin/signtx.rs and are not handled directly.
322324
Request::PrevtxInit(_)
@@ -1074,7 +1076,7 @@ mod tests {
10741076
root_fingerprint: keystore::root_fingerprint().unwrap(),
10751077
keypath: KEYPATH_ACCOUNT_TESTNET.to_vec(),
10761078
xpub: Some(
1077-
crate::keystore::get_xpub_once(KEYPATH_ACCOUNT_TESTNET)
1079+
crate::keystore::get_xpub_once(&mut TestingHal::new(), KEYPATH_ACCOUNT_TESTNET)
10781080
.unwrap()
10791081
.into(),
10801082
),
@@ -1083,7 +1085,7 @@ mod tests {
10831085
root_fingerprint: keystore::root_fingerprint().unwrap(),
10841086
keypath: KEYPATH_ACCOUNT_MAINNET.to_vec(),
10851087
xpub: Some(
1086-
crate::keystore::get_xpub_once(KEYPATH_ACCOUNT_MAINNET)
1088+
crate::keystore::get_xpub_once(&mut TestingHal::new(), KEYPATH_ACCOUNT_MAINNET)
10871089
.unwrap()
10881090
.into(),
10891091
),

src/rust/bitbox02-rust/src/hww/api/bitcoin/common.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,19 +81,20 @@ pub struct Payload {
8181

8282
impl Payload {
8383
pub fn from_simple(
84+
hal: &mut impl crate::hal::Hal,
8485
xpub_cache: &mut Bip32XpubCache,
8586
params: &Params,
8687
simple_type: SimpleType,
8788
keypath: &[u32],
8889
) -> Result<Self, Error> {
8990
match simple_type {
9091
SimpleType::P2wpkh => Ok(Payload {
91-
data: xpub_cache.get_xpub(keypath)?.pubkey_hash160(),
92+
data: xpub_cache.get_xpub(hal, keypath)?.pubkey_hash160(),
9293
output_type: BtcOutputType::P2wpkh,
9394
}),
9495
SimpleType::P2wpkhP2sh => {
9596
let payload_p2wpkh =
96-
Payload::from_simple(xpub_cache, params, SimpleType::P2wpkh, keypath)?;
97+
Payload::from_simple(hal, xpub_cache, params, SimpleType::P2wpkh, keypath)?;
9798
let pkscript_p2wpkh = payload_p2wpkh.pk_script(params)?;
9899
Ok(Payload {
99100
data: bitcoin::hashes::hash160::Hash::hash(&pkscript_p2wpkh)
@@ -106,7 +107,7 @@ impl Payload {
106107
if params.taproot_support {
107108
Ok(Payload {
108109
data: xpub_cache
109-
.get_xpub(keypath)?
110+
.get_xpub(hal, keypath)?
110111
.schnorr_bip86_pubkey()?
111112
.to_vec(),
112113
output_type: BtcOutputType::P2tr,
@@ -190,14 +191,15 @@ impl Payload {
190191
/// Computes the payload data from a script config. The payload can then be used generate a
191192
/// pkScript or an address.
192193
pub fn from(
194+
hal: &mut impl crate::hal::Hal,
193195
xpub_cache: &mut Bip32XpubCache,
194196
params: &Params,
195197
keypath: &[u32],
196198
script_config_account: &ValidatedScriptConfigWithKeypath,
197199
) -> Result<Self, Error> {
198200
match &script_config_account.config {
199201
ValidatedScriptConfig::SimpleType(simple_type) => {
200-
Self::from_simple(xpub_cache, params, *simple_type, keypath)
202+
Self::from_simple(hal, xpub_cache, params, *simple_type, keypath)
201203
}
202204
ValidatedScriptConfig::Multisig { multisig, .. } => Self::from_multisig(
203205
params,
@@ -582,6 +584,7 @@ mod tests {
582584
// p2wpkh
583585
assert_eq!(
584586
Payload::from_simple(
587+
&mut crate::hal::testing::TestingHal::new(),
585588
&mut xpub_cache,
586589
coin_params,
587590
SimpleType::P2wpkh,
@@ -596,6 +599,7 @@ mod tests {
596599
// p2wpkh-p2sh
597600
assert_eq!(
598601
Payload::from_simple(
602+
&mut crate::hal::testing::TestingHal::new(),
599603
&mut xpub_cache,
600604
coin_params,
601605
SimpleType::P2wpkhP2sh,
@@ -610,6 +614,7 @@ mod tests {
610614
// p2tr
611615
assert_eq!(
612616
Payload::from_simple(
617+
&mut crate::hal::testing::TestingHal::new(),
613618
&mut xpub_cache,
614619
coin_params,
615620
SimpleType::P2tr,

src/rust/bitbox02-rust/src/hww/api/bitcoin/multisig.rs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,11 @@ pub async fn confirm_extended(
259259
/// - no two xpubs are the same.
260260
///
261261
/// keypath: account-level keypath, e.g. m/48'/0'/10'/2'
262-
pub fn validate(multisig: &Multisig, keypath: &[u32]) -> Result<(), Error> {
262+
pub fn validate(
263+
hal: &mut impl crate::hal::Hal,
264+
multisig: &Multisig,
265+
keypath: &[u32],
266+
) -> Result<(), Error> {
263267
if multisig.xpubs.len() < 2 || multisig.xpubs.len() > MAX_SIGNERS {
264268
return Err(Error::InvalidInput);
265269
}
@@ -270,7 +274,7 @@ pub fn validate(multisig: &Multisig, keypath: &[u32]) -> Result<(), Error> {
270274
return Err(Error::InvalidInput);
271275
}
272276

273-
let our_xpub = crate::keystore::get_xpub_once(keypath)?.serialize(None)?;
277+
let our_xpub = crate::keystore::get_xpub_once(hal, keypath)?.serialize(None)?;
274278
let maybe_our_xpub =
275279
bip32::Xpub::from(&multisig.xpubs[multisig.our_xpub_index as usize]).serialize(None)?;
276280
if our_xpub != maybe_our_xpub {
@@ -589,18 +593,20 @@ mod tests {
589593
script_type: ScriptType::P2wsh as _,
590594
};
591595

596+
let mut mock_hal = crate::hal::testing::TestingHal::new();
597+
592598
// Keystore locked.
593599
crate::keystore::lock();
594-
assert!(validate(&multisig, keypath).is_err());
600+
assert!(validate(&mut mock_hal, &multisig, keypath).is_err());
595601

596602
// Ok.
597603
mock_unlocked_using_mnemonic(
598604
"sudden tenant fault inject concert weather maid people chunk youth stumble grit",
599605
"",
600606
);
601-
assert!(validate(&multisig, keypath).is_ok());
607+
assert!(validate(&mut mock_hal, &multisig, keypath).is_ok());
602608
// Ok at arbitrary keypath.
603-
assert!(validate(&Multisig {
609+
assert!(validate(&mut mock_hal,&Multisig {
604610
threshold: 1,
605611
xpubs: vec![
606612
parse_xpub("xpub6FMWuwbCA9KhoRzAMm63ZhLspk5S2DM5sePo8J8mQhcS1xyMbAqnc7Q7UescVEVFCS6qBMQLkEJWQ9Z3aDPgBov5nFUYxsJhwumsxM4npSo").unwrap(),
@@ -633,53 +639,53 @@ mod tests {
633639
"xpub6ECHc4kmTC2tQg2ZoAoazwyag9C4V6yFsZEhjwMJixdVNsUibot6uEvsZY38ZLVqWCtyc9gbzFEwHQLHCT8EiDDKSNNsFAB8NQYRgkiAQwu",
634640
"xpub6F7CaxXzBCtvXwpRi61KYyhBRkgT1856ujHV5AbJK6ySCUYoDruBH6Pnsi6eHkDiuKuAJ2tSc9x3emP7aax9Dc3u7nP7RCQXEjLKihQu6w1",
635641
].iter().map(|s| parse_xpub(s).unwrap()).collect();
636-
assert!(validate(&invalid, keypath).is_err());
642+
assert!(validate(&mut mock_hal, &invalid, keypath).is_err());
637643
}
638644

639645
{
640646
// number of cosigners too small
641647

642648
let mut invalid = multisig.clone();
643649
invalid.xpubs = vec![];
644-
assert!(validate(&invalid, keypath).is_err());
650+
assert!(validate(&mut mock_hal, &invalid, keypath).is_err());
645651
invalid.our_xpub_index = 0;
646652
invalid.xpubs = vec![parse_xpub(our_xpub_str).unwrap()];
647-
assert!(validate(&invalid, keypath).is_err());
653+
assert!(validate(&mut mock_hal, &invalid, keypath).is_err());
648654
}
649655

650656
{
651657
// threshold larger than number of cosigners
652658
let mut invalid = multisig.clone();
653659
invalid.threshold = 3;
654-
assert!(validate(&invalid, keypath).is_err());
660+
assert!(validate(&mut mock_hal, &invalid, keypath).is_err());
655661

656662
// threshold zero
657663
invalid.threshold = 0;
658-
assert!(validate(&invalid, keypath).is_err());
664+
assert!(validate(&mut mock_hal, &invalid, keypath).is_err());
659665
}
660666

661667
{
662668
// our xpub index larger than number of cosigners (xpubs[our_xpb_index] would be out of
663669
// bounds).
664670
let mut invalid = multisig.clone();
665671
invalid.our_xpub_index = 2;
666-
assert!(validate(&invalid, keypath).is_err());
672+
assert!(validate(&mut mock_hal, &invalid, keypath).is_err());
667673
}
668674

669675
{
670676
// our xpub is not part of the multisig (overwrite our xpub with an arbitrary other one).
671677

672678
let mut invalid = multisig.clone();
673679
invalid.xpubs[1] = parse_xpub("xpub6FNT7x2ZEBMhs4jvZJSEBV2qBCBnRidNsyqe7inT9V2wmEn4sqidTEudB4dVSvEjXz2NytcymwWJb8PPYExRycNf9SH8fAHzPWUsQJAmbR3").unwrap();
674-
assert!(validate(&invalid, keypath).is_err());
680+
assert!(validate(&mut mock_hal, &invalid, keypath).is_err());
675681
}
676682

677683
{
678684
// duplicate
679685

680686
let mut invalid = multisig.clone();
681687
invalid.xpubs[0] = invalid.xpubs[1].clone();
682-
assert!(validate(&invalid, keypath).is_err());
688+
assert!(validate(&mut mock_hal, &invalid, keypath).is_err());
683689
}
684690
}
685691

0 commit comments

Comments
 (0)