From e19255fff0ee862e145c7a0e3cac248cdd442b42 Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Wed, 18 Sep 2024 14:29:40 +0200 Subject: [PATCH] update protobuf messages From: https://github.com/BitBoxSwiss/bitbox02-firmware/commit/83cd6c8dbee7fc12dcb6b899fa428941550abc4b The new features (ETH address case, BTC payment requests & silent payments) need to be integrated separately. --- messages/btc.proto | 33 +++++++ messages/eth.proto | 8 ++ messages/hww.proto | 2 + messages/keystore.proto | 20 +++++ src/btc.rs | 2 + src/eth.rs | 2 + src/shiftcrypto.bitbox02.rs | 175 +++++++++++++++++++++++++++++++++++- 7 files changed, 239 insertions(+), 3 deletions(-) diff --git a/messages/btc.proto b/messages/btc.proto index 0e8d29b..6310343 100644 --- a/messages/btc.proto +++ b/messages/btc.proto @@ -24,6 +24,8 @@ enum BTCCoin { TBTC = 1; LTC = 2; TLTC = 3; + // Regtest + RBTC = 4; }; @@ -112,6 +114,7 @@ message BTCSignInitRequest { SAT = 1; } FormatUnit format_unit = 8; + bool contains_silent_payment_outputs = 9; } message BTCSignNextResponse { @@ -124,6 +127,7 @@ message BTCSignNextResponse { PREVTX_INPUT = 4; PREVTX_OUTPUT = 5; HOST_NONCE = 6; + PAYMENT_REQUEST = 7; } Type type = 1; // index of the current input or output @@ -134,6 +138,9 @@ message BTCSignNextResponse { // Previous tx's input/output index in case of PREV_INPUT or PREV_OUTPUT, for the input at `index`. uint32 prev_index = 5; AntiKleptoSignerCommitment anti_klepto_signer_commitment = 6; + // Generated output. The host *must* verify its correctness using `silent_payment_dleq_proof`. + bytes generated_output_pkscript = 7; + bytes silent_payment_dleq_proof = 8; } message BTCSignInputRequest { @@ -157,6 +164,10 @@ enum BTCOutputType { } message BTCSignOutputRequest { + // https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki + message SilentPayment { + string address = 1; + } bool ours = 1; BTCOutputType type = 2; // if ours is false // 20 bytes for p2pkh, p2sh, pw2wpkh. 32 bytes for p2wsh. @@ -165,6 +176,10 @@ message BTCSignOutputRequest { repeated uint32 keypath = 5; // if ours is true // If ours is true. References a script config from BTCSignInitRequest uint32 script_config_index = 6; + optional uint32 payment_request_index = 7; + // If provided, `type` and `payload` is ignored. The generated output pkScript is returned in + // BTCSignNextResponse. `contains_silent_payment_outputs` in the init request must be true. + SilentPayment silent_payment = 8; } message BTCScriptConfigRegistration { @@ -217,6 +232,23 @@ message BTCPrevTxOutputRequest { bytes pubkey_script = 2; } +message BTCPaymentRequestRequest { + message Memo { + message TextMemo { + string note = 1; + } + oneof memo { + TextMemo text_memo = 1; + } + } + + string recipient_name = 1; + repeated Memo memos = 2; + bytes nonce = 3; + uint64 total_amount = 4; + bytes signature = 5; +} + message BTCSignMessageRequest { BTCCoin coin = 1; BTCScriptConfigWithKeypath script_config = 2; @@ -238,6 +270,7 @@ message BTCRequest { BTCPrevTxOutputRequest prevtx_output = 5; BTCSignMessageRequest sign_message = 6; AntiKleptoSignatureRequest antiklepto_signature = 7; + BTCPaymentRequestRequest payment_request = 8; } } diff --git a/messages/eth.proto b/messages/eth.proto index c552e49..65c8f90 100644 --- a/messages/eth.proto +++ b/messages/eth.proto @@ -27,6 +27,12 @@ enum ETHCoin { RinkebyETH = 2; } +enum ETHAddressCase { + ETH_ADDRESS_CASE_MIXED = 0; + ETH_ADDRESS_CASE_UPPER = 1; + ETH_ADDRESS_CASE_LOWER = 2; +} + message ETHPubRequest { repeated uint32 keypath = 1; // Deprecated: use chain_id instead. @@ -56,6 +62,7 @@ message ETHSignRequest { AntiKleptoHostNonceCommitment host_nonce_commitment = 9; // If non-zero, `coin` is ignored and `chain_id` is used to identify the network. uint64 chain_id = 10; + ETHAddressCase address_case = 11; } // TX payload for an EIP-1559 (type 2) transaction: https://eips.ethereum.org/EIPS/eip-1559 @@ -70,6 +77,7 @@ message ETHSignEIP1559Request { bytes value = 8; // smallest big endian serialization, max. 32 bytes bytes data = 9; AntiKleptoHostNonceCommitment host_nonce_commitment = 10; + ETHAddressCase address_case = 11; } message ETHSignMessageRequest { diff --git a/messages/hww.proto b/messages/hww.proto index e3dbe8c..54c34f6 100644 --- a/messages/hww.proto +++ b/messages/hww.proto @@ -66,6 +66,7 @@ message Request { BTCRequest btc = 25; ElectrumEncryptionKeyRequest electrum_encryption_key = 26; CardanoRequest cardano = 27; + BIP85Request bip85 = 28; } } @@ -87,5 +88,6 @@ message Response { BTCResponse btc = 13; ElectrumEncryptionKeyResponse electrum_encryption_key = 14; CardanoResponse cardano = 15; + BIP85Response bip85 = 16; } } diff --git a/messages/keystore.proto b/messages/keystore.proto index 15f703f..d6d5641 100644 --- a/messages/keystore.proto +++ b/messages/keystore.proto @@ -4,6 +4,8 @@ syntax = "proto3"; package shiftcrypto.bitbox02; +import "google/protobuf/empty.proto"; + message ElectrumEncryptionKeyRequest { repeated uint32 keypath = 1; } @@ -11,3 +13,21 @@ message ElectrumEncryptionKeyRequest { message ElectrumEncryptionKeyResponse { string key = 1; } + +message BIP85Request { + message AppLn { + uint32 account_number = 1; + } + + oneof app { + google.protobuf.Empty bip39 = 1; + AppLn ln = 2; + } +} + +message BIP85Response { + oneof app { + google.protobuf.Empty bip39 = 1; + bytes ln = 2; + } +} diff --git a/src/btc.rs b/src/btc.rs index 37b0c60..f092807 100644 --- a/src/btc.rs +++ b/src/btc.rs @@ -696,6 +696,7 @@ impl PairedBitBox { num_outputs: transaction.outputs.len() as _, locktime: transaction.locktime, format_unit: format_unit as _, + contains_silent_payment_outputs: false, })) .await?; @@ -846,6 +847,7 @@ impl PairedBitBox { pb::btc_sign_next_response::Type::HostNonce => { return Err(Error::UnexpectedResponse); } + _ => return Err(Error::UnexpectedResponse), } } Ok(sigs) diff --git a/src/eth.rs b/src/eth.rs index de59b1e..4470d4b 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -483,6 +483,7 @@ impl PairedBitBox { commitment: crate::antiklepto::host_commit(&host_nonce).to_vec(), }), chain_id, + address_case: pb::EthAddressCase::Mixed as _, }); let response = self.query_proto_eth(request).await?; self.handle_antiklepto(&response, host_nonce).await @@ -515,6 +516,7 @@ impl PairedBitBox { host_nonce_commitment: Some(pb::AntiKleptoHostNonceCommitment { commitment: crate::antiklepto::host_commit(&host_nonce).to_vec(), }), + address_case: pb::EthAddressCase::Mixed as _, }); let response = self.query_proto_eth(request).await?; self.handle_antiklepto(&response, host_nonce).await diff --git a/src/shiftcrypto.bitbox02.rs b/src/shiftcrypto.bitbox02.rs index bd320fd..8fd73aa 100644 --- a/src/shiftcrypto.bitbox02.rs +++ b/src/shiftcrypto.bitbox02.rs @@ -559,6 +559,8 @@ pub struct BtcSignInitRequest { pub locktime: u32, #[prost(enumeration = "btc_sign_init_request::FormatUnit", tag = "8")] pub format_unit: i32, + #[prost(bool, tag = "9")] + pub contains_silent_payment_outputs: bool, } /// Nested message and enum types in `BTCSignInitRequest`. pub mod btc_sign_init_request { @@ -626,6 +628,11 @@ pub struct BtcSignNextResponse { pub anti_klepto_signer_commitment: ::core::option::Option< AntiKleptoSignerCommitment, >, + /// Generated output. The host *must* verify its correctness using `silent_payment_dleq_proof`. + #[prost(bytes = "vec", tag = "7")] + pub generated_output_pkscript: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "8")] + pub silent_payment_dleq_proof: ::prost::alloc::vec::Vec, } /// Nested message and enum types in `BTCSignNextResponse`. pub mod btc_sign_next_response { @@ -652,6 +659,7 @@ pub mod btc_sign_next_response { PrevtxInput = 4, PrevtxOutput = 5, HostNonce = 6, + PaymentRequest = 7, } impl Type { /// String value of the enum field names used in the ProtoBuf definition. @@ -667,6 +675,7 @@ pub mod btc_sign_next_response { Type::PrevtxInput => "PREVTX_INPUT", Type::PrevtxOutput => "PREVTX_OUTPUT", Type::HostNonce => "HOST_NONCE", + Type::PaymentRequest => "PAYMENT_REQUEST", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -679,6 +688,7 @@ pub mod btc_sign_next_response { "PREVTX_INPUT" => Some(Self::PrevtxInput), "PREVTX_OUTPUT" => Some(Self::PrevtxOutput), "HOST_NONCE" => Some(Self::HostNonce), + "PAYMENT_REQUEST" => Some(Self::PaymentRequest), _ => None, } } @@ -737,6 +747,24 @@ pub struct BtcSignOutputRequest { /// If ours is true. References a script config from BTCSignInitRequest #[prost(uint32, tag = "6")] pub script_config_index: u32, + #[prost(uint32, optional, tag = "7")] + pub payment_request_index: ::core::option::Option, + /// If provided, `type` and `payload` is ignored. The generated output pkScript is returned in + /// BTCSignNextResponse. `contains_silent_payment_outputs` in the init request must be true. + #[prost(message, optional, tag = "8")] + pub silent_payment: ::core::option::Option, +} +/// Nested message and enum types in `BTCSignOutputRequest`. +pub mod btc_sign_output_request { + /// + #[cfg_attr(feature = "wasm", derive(serde::Serialize, serde::Deserialize))] + #[cfg_attr(feature = "wasm", serde(rename_all = "camelCase"))] + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct SilentPayment { + #[prost(string, tag = "1")] + pub address: ::prost::alloc::string::String, + } } #[cfg_attr(feature = "wasm", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "wasm", serde(rename_all = "camelCase"))] @@ -874,6 +902,52 @@ pub struct BtcPrevTxOutputRequest { #[cfg_attr(feature = "wasm", serde(rename_all = "camelCase"))] #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] +pub struct BtcPaymentRequestRequest { + #[prost(string, tag = "1")] + pub recipient_name: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub memos: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "3")] + pub nonce: ::prost::alloc::vec::Vec, + #[prost(uint64, tag = "4")] + pub total_amount: u64, + #[prost(bytes = "vec", tag = "5")] + pub signature: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `BTCPaymentRequestRequest`. +pub mod btc_payment_request_request { + #[cfg_attr(feature = "wasm", derive(serde::Serialize, serde::Deserialize))] + #[cfg_attr(feature = "wasm", serde(rename_all = "camelCase"))] + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Memo { + #[prost(oneof = "memo::Memo", tags = "1")] + pub memo: ::core::option::Option, + } + /// Nested message and enum types in `Memo`. + pub mod memo { + #[cfg_attr(feature = "wasm", derive(serde::Serialize, serde::Deserialize))] + #[cfg_attr(feature = "wasm", serde(rename_all = "camelCase"))] + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct TextMemo { + #[prost(string, tag = "1")] + pub note: ::prost::alloc::string::String, + } + #[cfg_attr(feature = "wasm", derive(serde::Serialize, serde::Deserialize))] + #[cfg_attr(feature = "wasm", serde(rename_all = "camelCase"))] + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Memo { + #[prost(message, tag = "1")] + TextMemo(TextMemo), + } + } +} +#[cfg_attr(feature = "wasm", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "wasm", serde(rename_all = "camelCase"))] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] pub struct BtcSignMessageRequest { #[prost(enumeration = "BtcCoin", tag = "1")] pub coin: i32, @@ -898,7 +972,7 @@ pub struct BtcSignMessageResponse { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct BtcRequest { - #[prost(oneof = "btc_request::Request", tags = "1, 2, 3, 4, 5, 6, 7")] + #[prost(oneof = "btc_request::Request", tags = "1, 2, 3, 4, 5, 6, 7, 8")] pub request: ::core::option::Option, } /// Nested message and enum types in `BTCRequest`. @@ -922,6 +996,8 @@ pub mod btc_request { SignMessage(super::BtcSignMessageRequest), #[prost(message, tag = "7")] AntikleptoSignature(super::AntiKleptoSignatureRequest), + #[prost(message, tag = "8")] + PaymentRequest(super::BtcPaymentRequestRequest), } } #[cfg_attr(feature = "wasm", derive(serde::Serialize, serde::Deserialize))] @@ -960,6 +1036,8 @@ pub enum BtcCoin { Tbtc = 1, Ltc = 2, Tltc = 3, + /// Regtest + Rbtc = 4, } impl BtcCoin { /// String value of the enum field names used in the ProtoBuf definition. @@ -972,6 +1050,7 @@ impl BtcCoin { BtcCoin::Tbtc => "TBTC", BtcCoin::Ltc => "LTC", BtcCoin::Tltc => "TLTC", + BtcCoin::Rbtc => "RBTC", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -981,6 +1060,7 @@ impl BtcCoin { "TBTC" => Some(Self::Tbtc), "LTC" => Some(Self::Ltc), "TLTC" => Some(Self::Tltc), + "RBTC" => Some(Self::Rbtc), _ => None, } } @@ -1453,6 +1533,8 @@ pub struct EthSignRequest { /// If non-zero, `coin` is ignored and `chain_id` is used to identify the network. #[prost(uint64, tag = "10")] pub chain_id: u64, + #[prost(enumeration = "EthAddressCase", tag = "11")] + pub address_case: i32, } /// TX payload for an EIP-1559 (type 2) transaction: #[cfg_attr(feature = "wasm", derive(serde::Serialize, serde::Deserialize))] @@ -1490,6 +1572,8 @@ pub struct EthSignEip1559Request { pub data: ::prost::alloc::vec::Vec, #[prost(message, optional, tag = "10")] pub host_nonce_commitment: ::core::option::Option, + #[prost(enumeration = "EthAddressCase", tag = "11")] + pub address_case: i32, } #[cfg_attr(feature = "wasm", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "wasm", serde(rename_all = "camelCase"))] @@ -1794,6 +1878,37 @@ impl EthCoin { } #[cfg_attr(feature = "wasm", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "wasm", serde(rename_all = "camelCase"))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum EthAddressCase { + Mixed = 0, + Upper = 1, + Lower = 2, +} +impl EthAddressCase { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + EthAddressCase::Mixed => "ETH_ADDRESS_CASE_MIXED", + EthAddressCase::Upper => "ETH_ADDRESS_CASE_UPPER", + EthAddressCase::Lower => "ETH_ADDRESS_CASE_LOWER", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "ETH_ADDRESS_CASE_MIXED" => Some(Self::Mixed), + "ETH_ADDRESS_CASE_UPPER" => Some(Self::Upper), + "ETH_ADDRESS_CASE_LOWER" => Some(Self::Lower), + _ => None, + } + } +} +#[cfg_attr(feature = "wasm", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "wasm", serde(rename_all = "camelCase"))] #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ElectrumEncryptionKeyRequest { @@ -1816,6 +1931,56 @@ pub struct ElectrumEncryptionKeyResponse { #[cfg_attr(feature = "wasm", serde(rename_all = "camelCase"))] #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct Bip85Request { + #[prost(oneof = "bip85_request::App", tags = "1, 2")] + pub app: ::core::option::Option, +} +/// Nested message and enum types in `BIP85Request`. +pub mod bip85_request { + #[cfg_attr(feature = "wasm", derive(serde::Serialize, serde::Deserialize))] + #[cfg_attr(feature = "wasm", serde(rename_all = "camelCase"))] + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, Copy, PartialEq, ::prost::Message)] + pub struct AppLn { + #[prost(uint32, tag = "1")] + pub account_number: u32, + } + #[cfg_attr(feature = "wasm", derive(serde::Serialize, serde::Deserialize))] + #[cfg_attr(feature = "wasm", serde(rename_all = "camelCase"))] + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, Copy, PartialEq, ::prost::Oneof)] + pub enum App { + #[prost(message, tag = "1")] + Bip39(()), + #[prost(message, tag = "2")] + Ln(AppLn), + } +} +#[cfg_attr(feature = "wasm", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "wasm", serde(rename_all = "camelCase"))] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Bip85Response { + #[prost(oneof = "bip85_response::App", tags = "1, 2")] + pub app: ::core::option::Option, +} +/// Nested message and enum types in `BIP85Response`. +pub mod bip85_response { + #[cfg_attr(feature = "wasm", derive(serde::Serialize, serde::Deserialize))] + #[cfg_attr(feature = "wasm", serde(rename_all = "camelCase"))] + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum App { + #[prost(message, tag = "1")] + Bip39(()), + #[prost(bytes, tag = "2")] + Ln(::prost::alloc::vec::Vec), + } +} +#[cfg_attr(feature = "wasm", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "wasm", serde(rename_all = "camelCase"))] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct ShowMnemonicRequest {} #[cfg_attr(feature = "wasm", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "wasm", serde(rename_all = "camelCase"))] @@ -1933,7 +2098,7 @@ pub struct Success {} pub struct Request { #[prost( oneof = "request::Request", - tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27" + tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28" )] pub request: ::core::option::Option, } @@ -1996,6 +2161,8 @@ pub mod request { ElectrumEncryptionKey(super::ElectrumEncryptionKeyRequest), #[prost(message, tag = "27")] Cardano(super::CardanoRequest), + #[prost(message, tag = "28")] + Bip85(super::Bip85Request), } } #[cfg_attr(feature = "wasm", derive(serde::Serialize, serde::Deserialize))] @@ -2005,7 +2172,7 @@ pub mod request { pub struct Response { #[prost( oneof = "response::Response", - tags = "1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15" + tags = "1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16" )] pub response: ::core::option::Option, } @@ -2045,5 +2212,7 @@ pub mod response { ElectrumEncryptionKey(super::ElectrumEncryptionKeyResponse), #[prost(message, tag = "15")] Cardano(super::CardanoResponse), + #[prost(message, tag = "16")] + Bip85(super::Bip85Response), } }