Skip to content

Commit d1ebf2c

Browse files
committed
Merge branch 'signmsg-testnet'
2 parents 7c21e30 + 59c7f82 commit d1ebf2c

File tree

5 files changed

+120
-26
lines changed

5 files changed

+120
-26
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ customers cannot upgrade their bootloader, its changes are recorded separately.
99
### [Unreleased]
1010
- Ethereum: add confirmation screen for known networks, change base unit to ETH for Arbitrum and Optimism
1111
- Ethereum: add Base and Gnosis Chain to known networks
12+
- Bitcoin: enable message signing on testnet and regtest
1213

1314
### 9.22.0
1415
- Update manufacturer HID descriptor to bitbox.swiss

CMakeLists.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ endif()
9191
#
9292
# Versions MUST contain three parts and start with lowercase 'v'.
9393
# Example 'v1.0.0'. They MUST not contain a pre-release label such as '-beta'.
94-
set(FIRMWARE_VERSION "v9.22.0")
95-
set(FIRMWARE_BTC_ONLY_VERSION "v9.22.0")
94+
set(FIRMWARE_VERSION "v9.23.0")
95+
set(FIRMWARE_BTC_ONLY_VERSION "v9.23.0")
9696
set(BOOTLOADER_VERSION "v1.1.0")
9797

9898
find_package(PythonInterp 3.6 REQUIRED)

py/bitbox02/bitbox02/bitbox02/bitbox02.py

+2
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,8 @@ def btc_sign_msg(
613613
# pylint: disable=no-member
614614

615615
self._require_atleast(semver.VersionInfo(9, 2, 0))
616+
if coin in (btc.TBTC, btc.RBTC):
617+
self._require_atleast(semver.VersionInfo(9, 23, 0))
616618

617619
request = btc.BTCRequest()
618620
request.sign_message.CopyFrom(

py/send_message.py

+49-23
Original file line numberDiff line numberDiff line change
@@ -896,33 +896,59 @@ def _sign_btc_tx(self) -> None:
896896
def _sign_btc_message(self) -> None:
897897
# pylint: disable=no-member
898898

899-
keypath = [49 + HARDENED, 0 + HARDENED, 0 + HARDENED, 0, 0]
900-
script_config = bitbox02.btc.BTCScriptConfig(
901-
simple_type=bitbox02.btc.BTCScriptConfig.P2WPKH_P2SH
902-
)
903-
address = self._device.btc_address(
904-
keypath=keypath, script_config=script_config, display=False
905-
)
899+
def sign(
900+
coin: "bitbox02.btc.BTCCoin.V",
901+
keypath: Sequence[int],
902+
script_config: bitbox02.btc.BTCScriptConfig,
903+
) -> None:
904+
address = self._device.btc_address(
905+
coin=coin, keypath=keypath, script_config=script_config, display=False
906+
)
906907

907-
print("Address:", address)
908+
print("Address:", address)
908909

909-
msg = input(r"Message to sign (\n = newline): ")
910-
if msg.startswith("0x"):
911-
msg_bytes = binascii.unhexlify(msg[2:])
912-
else:
913-
msg_bytes = msg.replace(r"\n", "\n").encode("utf-8")
910+
msg = input(r"Message to sign (\n = newline): ")
911+
if msg.startswith("0x"):
912+
msg_bytes = binascii.unhexlify(msg[2:])
913+
else:
914+
msg_bytes = msg.replace(r"\n", "\n").encode("utf-8")
914915

915-
try:
916-
_, _, sig65 = self._device.btc_sign_msg(
917-
bitbox02.btc.BTC,
918-
bitbox02.btc.BTCScriptConfigWithKeypath(
919-
script_config=script_config, keypath=keypath
920-
),
921-
msg_bytes,
916+
try:
917+
_, _, sig65 = self._device.btc_sign_msg(
918+
coin,
919+
bitbox02.btc.BTCScriptConfigWithKeypath(
920+
script_config=script_config, keypath=keypath
921+
),
922+
msg_bytes,
923+
)
924+
print("Signature:", base64.b64encode(sig65).decode("ascii"))
925+
except UserAbortException:
926+
print("Aborted by user")
927+
928+
def sign_mainnet() -> None:
929+
keypath = [49 + HARDENED, 0 + HARDENED, 0 + HARDENED, 0, 0]
930+
script_config = bitbox02.btc.BTCScriptConfig(
931+
simple_type=bitbox02.btc.BTCScriptConfig.P2WPKH_P2SH
922932
)
923-
print("Signature:", base64.b64encode(sig65).decode("ascii"))
924-
except UserAbortException:
925-
print("Aborted by user")
933+
sign(bitbox02.btc.BTC, keypath, script_config)
934+
935+
def sign_testnet() -> None:
936+
keypath = [49 + HARDENED, 1 + HARDENED, 0 + HARDENED, 0, 0]
937+
script_config = bitbox02.btc.BTCScriptConfig(
938+
simple_type=bitbox02.btc.BTCScriptConfig.P2WPKH_P2SH
939+
)
940+
sign(bitbox02.btc.TBTC, keypath, script_config)
941+
942+
choices = (
943+
("Mainnet", sign_mainnet),
944+
("Testnet", sign_testnet),
945+
)
946+
choice = ask_user(choices)
947+
if callable(choice):
948+
try:
949+
choice()
950+
except UserAbortException:
951+
eprint("Aborted by user")
926952

927953
def _check_backup(self) -> None:
928954
print("Your BitBox02 will now perform a backup check")

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

+66-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const MAX_MESSAGE_SIZE: usize = 1024;
3636
/// compact format (R and S values), and the last byte is the recoverable id (recid).
3737
pub async fn process(request: &pb::BtcSignMessageRequest) -> Result<Response, Error> {
3838
let coin = BtcCoin::try_from(request.coin)?;
39-
if coin != BtcCoin::Btc {
39+
if !matches!(coin, BtcCoin::Btc | BtcCoin::Tbtc | BtcCoin::Rbtc) {
4040
return Err(Error::InvalidInput);
4141
}
4242
let (keypath, simple_type) = match &request.script_config {
@@ -182,6 +182,52 @@ mod tests {
182182
);
183183
}
184184

185+
#[test]
186+
pub fn test_p2wpkh_testnet() {
187+
let request = pb::BtcSignMessageRequest {
188+
coin: BtcCoin::Tbtc as _,
189+
script_config: Some(pb::BtcScriptConfigWithKeypath {
190+
script_config: Some(pb::BtcScriptConfig {
191+
config: Some(Config::SimpleType(SimpleType::P2wpkh as _)),
192+
}),
193+
keypath: vec![84 + HARDENED, 1 + HARDENED, 0 + HARDENED, 0, 0],
194+
}),
195+
msg: MESSAGE.to_vec(),
196+
host_nonce_commitment: None,
197+
};
198+
199+
static mut CONFIRM_COUNTER: u32 = 0;
200+
201+
mock(Data {
202+
ui_confirm_create: Some(Box::new(|params| {
203+
match unsafe {
204+
CONFIRM_COUNTER += 1;
205+
CONFIRM_COUNTER
206+
} {
207+
1 => {
208+
assert_eq!(params.title, "Sign message");
209+
assert_eq!(params.body, "Coin: BTC Testnet");
210+
true
211+
}
212+
2 => {
213+
assert_eq!(params.title, "Address");
214+
assert_eq!(params.body, "tb1qnlyrq9pshg0v0lsuudjgga4nvmjxhcvketqwdg");
215+
true
216+
}
217+
3 => {
218+
assert_eq!(params.title, "Sign message");
219+
assert_eq!(params.body.as_bytes(), MESSAGE);
220+
true
221+
}
222+
_ => panic!("too many user confirmations"),
223+
}
224+
})),
225+
..Default::default()
226+
});
227+
mock_unlocked();
228+
assert!(block_on(process(&request)).is_ok());
229+
}
230+
185231
#[test]
186232
pub fn test_p2wpkh_p2sh() {
187233
let request = pb::BtcSignMessageRequest {
@@ -419,5 +465,24 @@ mod tests {
419465
})),
420466
Err(Error::InvalidInput)
421467
);
468+
// Invalid keypath (mainnet keypath on testnet)
469+
mock(Data {
470+
..Default::default()
471+
});
472+
mock_unlocked();
473+
assert_eq!(
474+
block_on(process(&pb::BtcSignMessageRequest {
475+
coin: BtcCoin::Tbtc as _,
476+
script_config: Some(pb::BtcScriptConfigWithKeypath {
477+
script_config: Some(pb::BtcScriptConfig {
478+
config: Some(Config::SimpleType(SimpleType::P2wpkh as _))
479+
}),
480+
keypath: vec![84 + HARDENED, 0 + HARDENED, 0 + HARDENED, 0, 0],
481+
}),
482+
msg: MESSAGE.to_vec(),
483+
host_nonce_commitment: None,
484+
})),
485+
Err(Error::InvalidInput)
486+
);
422487
}
423488
}

0 commit comments

Comments
 (0)