diff --git a/CHANGELOG.md b/CHANGELOG.md index a04650ef5..54375dc6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ customers cannot upgrade their bootloader, its changes are recorded separately. ## Firmware ### [Unreleased] +- Add ability to display the root fingerprint on the device ### 9.10.0 [2022-03-10] - Bitcoin: add support for BIP-86: receive to taproot addresses and sign transactions with taproot inputs diff --git a/CMakeLists.txt b/CMakeLists.txt index cd8accbdb..c7b05b95b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,8 +89,8 @@ endif() # # Versions MUST contain three parts and start with lowercase 'v'. # Example 'v1.0.0'. They MUST not contain a pre-release label such as '-beta'. -set(FIRMWARE_VERSION "v9.10.0") -set(FIRMWARE_BTC_ONLY_VERSION "v9.10.0") +set(FIRMWARE_VERSION "v9.11.0") +set(FIRMWARE_BTC_ONLY_VERSION "v9.11.0") set(BOOTLOADER_VERSION "v1.0.4") find_package(PythonInterp 3.6 REQUIRED) diff --git a/messages/common.proto b/messages/common.proto index f16755de5..aad7859ff 100644 --- a/messages/common.proto +++ b/messages/common.proto @@ -20,6 +20,7 @@ message PubResponse { } message RootFingerprintRequest { + bool display = 1; } message RootFingerprintResponse { diff --git a/py/bitbox02/CHANGELOG.md b/py/bitbox02/CHANGELOG.md index 4f779976a..f1ce57504 100644 --- a/py/bitbox02/CHANGELOG.md +++ b/py/bitbox02/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## [Unreleased] +- Add `display` parameter to `root_fingerprint()`. + ## 6.0.0 - Offset the recoverable ID by 27 in the signature returned by `eth_sign_msg()`. - Rename `BTCOutputExternal.hash` to `BTCOutputExternal.payload`. diff --git a/py/bitbox02/bitbox02/bitbox02/__init__.py b/py/bitbox02/bitbox02/bitbox02/__init__.py index f8f29ca5e..ca3b80941 100644 --- a/py/bitbox02/bitbox02/bitbox02/__init__.py +++ b/py/bitbox02/bitbox02/bitbox02/__init__.py @@ -16,7 +16,7 @@ from __future__ import print_function import sys -__version__ = "6.0.0" +__version__ = "6.1.0" if sys.version_info.major != 3 or sys.version_info.minor < 6: print( diff --git a/py/bitbox02/bitbox02/bitbox02/bitbox02.py b/py/bitbox02/bitbox02/bitbox02/bitbox02.py index c87c141df..79450b48e 100644 --- a/py/bitbox02/bitbox02/bitbox02/bitbox02.py +++ b/py/bitbox02/bitbox02/bitbox02/bitbox02.py @@ -656,13 +656,13 @@ def remove_sdcard(self) -> None: ) self._msg_query(request, expected_response="success") - def root_fingerprint(self) -> bytes: + def root_fingerprint(self, display: bool = False) -> bytes: """ - Get the root fingerprint from the bitbox02 + Get the root fingerprint from the bitbox02. If `display` is `True`, the fingerprint is displayed on the device before returning (only from firmware v9.11.0). """ # pylint: disable=no-member request = hww.Request() - request.fingerprint.CopyFrom(common.RootFingerprintRequest()) + request.fingerprint.CopyFrom(common.RootFingerprintRequest(display=display)) response = self._msg_query(request, expected_response="fingerprint") return response.fingerprint.fingerprint diff --git a/py/bitbox02/bitbox02/communication/generated/common_pb2.py b/py/bitbox02/bitbox02/communication/generated/common_pb2.py index 2182ceeca..f4dd49e60 100644 --- a/py/bitbox02/bitbox02/communication/generated/common_pb2.py +++ b/py/bitbox02/bitbox02/communication/generated/common_pb2.py @@ -19,7 +19,7 @@ name='common.proto', package='shiftcrypto.bitbox02', syntax='proto3', - serialized_pb=_b('\n\x0c\x63ommon.proto\x12\x14shiftcrypto.bitbox02\"\x1a\n\x0bPubResponse\x12\x0b\n\x03pub\x18\x01 \x01(\t\"\x18\n\x16RootFingerprintRequest\".\n\x17RootFingerprintResponse\x12\x13\n\x0b\x66ingerprint\x18\x01 \x01(\x0c\"l\n\x04XPub\x12\r\n\x05\x64\x65pth\x18\x01 \x01(\x0c\x12\x1a\n\x12parent_fingerprint\x18\x02 \x01(\x0c\x12\x11\n\tchild_num\x18\x03 \x01(\r\x12\x12\n\nchain_code\x18\x04 \x01(\x0c\x12\x12\n\npublic_key\x18\x05 \x01(\x0c\"\x1a\n\x07Keypath\x12\x0f\n\x07keypath\x18\x01 \x03(\rb\x06proto3') + serialized_pb=_b('\n\x0c\x63ommon.proto\x12\x14shiftcrypto.bitbox02\"\x1a\n\x0bPubResponse\x12\x0b\n\x03pub\x18\x01 \x01(\t\")\n\x16RootFingerprintRequest\x12\x0f\n\x07\x64isplay\x18\x01 \x01(\x08\".\n\x17RootFingerprintResponse\x12\x13\n\x0b\x66ingerprint\x18\x01 \x01(\x0c\"l\n\x04XPub\x12\r\n\x05\x64\x65pth\x18\x01 \x01(\x0c\x12\x1a\n\x12parent_fingerprint\x18\x02 \x01(\x0c\x12\x11\n\tchild_num\x18\x03 \x01(\r\x12\x12\n\nchain_code\x18\x04 \x01(\x0c\x12\x12\n\npublic_key\x18\x05 \x01(\x0c\"\x1a\n\x07Keypath\x12\x0f\n\x07keypath\x18\x01 \x03(\rb\x06proto3') ) _sym_db.RegisterFileDescriptor(DESCRIPTOR) @@ -64,6 +64,13 @@ file=DESCRIPTOR, containing_type=None, fields=[ + _descriptor.FieldDescriptor( + name='display', full_name='shiftcrypto.bitbox02.RootFingerprintRequest.display', index=0, + number=1, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), ], extensions=[ ], @@ -77,7 +84,7 @@ oneofs=[ ], serialized_start=66, - serialized_end=90, + serialized_end=107, ) @@ -107,8 +114,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=92, - serialized_end=138, + serialized_start=109, + serialized_end=155, ) @@ -166,8 +173,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=140, - serialized_end=248, + serialized_start=157, + serialized_end=265, ) @@ -197,8 +204,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=250, - serialized_end=276, + serialized_start=267, + serialized_end=293, ) DESCRIPTOR.message_types_by_name['PubResponse'] = _PUBRESPONSE diff --git a/py/bitbox02/bitbox02/communication/generated/common_pb2.pyi b/py/bitbox02/bitbox02/communication/generated/common_pb2.pyi index 2ddc69285..0b88751f4 100644 --- a/py/bitbox02/bitbox02/communication/generated/common_pb2.pyi +++ b/py/bitbox02/bitbox02/communication/generated/common_pb2.pyi @@ -24,8 +24,13 @@ global___PubResponse = PubResponse class RootFingerprintRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor = ... + DISPLAY_FIELD_NUMBER: builtins.int + display: builtins.bool = ... def __init__(self, + *, + display : builtins.bool = ..., ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["display",b"display"]) -> None: ... global___RootFingerprintRequest = RootFingerprintRequest class RootFingerprintResponse(google.protobuf.message.Message): diff --git a/py/send_message.py b/py/send_message.py index c4a62bd94..242ede204 100755 --- a/py/send_message.py +++ b/py/send_message.py @@ -275,6 +275,7 @@ def _remove_sdcard(self) -> None: def _get_root_fingerprint(self) -> None: print(f"Root fingerprint: {self._device.root_fingerprint().hex()}") + self._device.root_fingerprint(display=True) def _display_zpub(self) -> None: try: diff --git a/src/rust/bitbox02-rust/src/hww/api.rs b/src/rust/bitbox02-rust/src/hww/api.rs index 313bec8c4..6c9b4a8ed 100644 --- a/src/rust/bitbox02-rust/src/hww/api.rs +++ b/src/rust/bitbox02-rust/src/hww/api.rs @@ -155,7 +155,7 @@ async fn process_api(request: &Request) -> Option> { #[cfg(not(feature = "app-ethereum"))] Request::Eth(_) => Some(Err(Error::Disabled)), - Request::Fingerprint(pb::RootFingerprintRequest {}) => Some(rootfingerprint::process()), + Request::Fingerprint(ref request) => Some(rootfingerprint::process(request).await), request @ Request::BtcPub(_) | request @ Request::Btc(_) | request @ Request::BtcSignInit(_) => process_api_btc(request).await, diff --git a/src/rust/bitbox02-rust/src/hww/api/rootfingerprint.rs b/src/rust/bitbox02-rust/src/hww/api/rootfingerprint.rs index 10a59336b..4eb47c678 100644 --- a/src/rust/bitbox02-rust/src/hww/api/rootfingerprint.rs +++ b/src/rust/bitbox02-rust/src/hww/api/rootfingerprint.rs @@ -19,11 +19,21 @@ use pb::response::Response; use bitbox02::keystore; +use crate::workflow::confirm; + /// Returns the keystore's root fingerprint, which is the first 32 /// bits of the hash160 of the pubkey at the keypath m/. /// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#key-identifiers -pub fn process() -> Result { +pub async fn process(request: &pb::RootFingerprintRequest) -> Result { let fingerprint = keystore::root_fingerprint()?; + if request.display { + confirm::confirm(&confirm::Params { + body: &format!("Root fingerprint:\n{}", &hex::encode(&fingerprint[..])), + accept_only: true, + ..Default::default() + }) + .await?; + } Ok(Response::Fingerprint(pb::RootFingerprintResponse { fingerprint: fingerprint.to_vec(), })) @@ -33,32 +43,55 @@ pub fn process() -> Result { mod tests { use super::*; + use crate::bb02_async::block_on; + use alloc::boxed::Box; use bitbox02::keystore::lock; - use bitbox02::testing::mock_unlocked_using_mnemonic; + use bitbox02::testing::{mock, mock_unlocked_using_mnemonic, Data}; #[test] fn test_process() { lock(); - assert_eq!(process(), Err(Error::Generic)); + assert_eq!( + block_on(process(&pb::RootFingerprintRequest { display: false })), + Err(Error::Generic) + ); mock_unlocked_using_mnemonic( "purity concert above invest pigeon category peace tuition hazard vivid latin since legal speak nation session onion library travel spell region blast estate stay" ); assert_eq!( - process(), + block_on(process(&pb::RootFingerprintRequest { display: false })), Ok(Response::Fingerprint(pb::RootFingerprintResponse { fingerprint: vec![0x02, 0x40, 0xe9, 0x2a], })) ); + static mut CONFIRM_COUNTER: u32 = 0; + mock(Data { + ui_confirm_create: Some(Box::new(|params| { + match unsafe { + CONFIRM_COUNTER += 1; + CONFIRM_COUNTER + } { + 1 => { + assert_eq!(params.title, ""); + assert_eq!(params.body, "Root fingerprint:\nf40b469a"); + true + } + _ => panic!("too many user confirmations"), + } + })), + ..Default::default() + }); mock_unlocked_using_mnemonic( "small agent wife animal marine cloth exit thank stool idea steel frame", ); assert_eq!( - process(), + block_on(process(&pb::RootFingerprintRequest { display: true })), Ok(Response::Fingerprint(pb::RootFingerprintResponse { fingerprint: vec![0xf4, 0x0b, 0x46, 0x9a], })) ); + assert_eq!(unsafe { CONFIRM_COUNTER }, 1); } } diff --git a/src/rust/bitbox02-rust/src/shiftcrypto.bitbox02.rs b/src/rust/bitbox02-rust/src/shiftcrypto.bitbox02.rs index 542094c42..129fd95d9 100644 --- a/src/rust/bitbox02-rust/src/shiftcrypto.bitbox02.rs +++ b/src/rust/bitbox02-rust/src/shiftcrypto.bitbox02.rs @@ -5,6 +5,8 @@ pub struct PubResponse { } #[derive(Clone, PartialEq, ::prost::Message)] pub struct RootFingerprintRequest { + #[prost(bool, tag="1")] + pub display: bool, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct RootFingerprintResponse {