Skip to content

Commit 83cd6c8

Browse files
committed
Merge branch 'sp'
2 parents f819645 + 3b91825 commit 83cd6c8

File tree

197 files changed

+58327
-104
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

197 files changed

+58327
-104
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ customers cannot upgrade their bootloader, its changes are recorded separately.
77
## Firmware
88

99
### [Unreleased]
10+
- Bitcoin: add support for sending to silent payment (BIP-352) addresses
1011
- Bitcoin: add support for regtest
1112

1213
### 9.20.0

external/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ string(REPLACE "-mfpu=fpv4-sp-d16" "" MODIFIED_C_FLAGS ${MODIFIED_C_FLAGS_TMP})
1717
# wally-core
1818

1919
# configure flags for secp256k1 bundled in libwally core, to reduce memory consumption
20-
set(LIBWALLY_SECP256k1_FLAGS --with-ecmult-window=2 --with-ecmult-gen-precision=2 --enable-ecmult-static-precomputation --enable-module-schnorrsig)
20+
set(LIBWALLY_SECP256k1_FLAGS --with-ecmult-window=2 --with-ecmult-gen-precision=2 --enable-ecmult-static-precomputation --enable-module-schnorrsig --enable-module-ecdsa-adaptor)
2121
set(LIBWALLY_CONFIGURE_FLAGS --enable-static --disable-shared --disable-tests ${LIBWALLY_SECP256k1_FLAGS})
2222
if(SANITIZE_ADDRESS)
2323
set(LIBWALLY_CFLAGS "-fsanitize=address")

messages/btc.proto

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ message BTCSignInitRequest {
114114
SAT = 1;
115115
}
116116
FormatUnit format_unit = 8;
117+
bool contains_silent_payment_outputs = 9;
117118
}
118119

119120
message BTCSignNextResponse {
@@ -137,6 +138,9 @@ message BTCSignNextResponse {
137138
// Previous tx's input/output index in case of PREV_INPUT or PREV_OUTPUT, for the input at `index`.
138139
uint32 prev_index = 5;
139140
AntiKleptoSignerCommitment anti_klepto_signer_commitment = 6;
141+
// Generated output. The host *must* verify its correctness using `silent_payment_dleq_proof`.
142+
bytes generated_output_pkscript = 7;
143+
bytes silent_payment_dleq_proof = 8;
140144
}
141145

142146
message BTCSignInputRequest {
@@ -160,6 +164,10 @@ enum BTCOutputType {
160164
}
161165

162166
message BTCSignOutputRequest {
167+
// https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki
168+
message SilentPayment {
169+
string address = 1;
170+
}
163171
bool ours = 1;
164172
BTCOutputType type = 2; // if ours is false
165173
// 20 bytes for p2pkh, p2sh, pw2wpkh. 32 bytes for p2wsh.
@@ -169,6 +177,9 @@ message BTCSignOutputRequest {
169177
// If ours is true. References a script config from BTCSignInitRequest
170178
uint32 script_config_index = 6;
171179
optional uint32 payment_request_index = 7;
180+
// If provided, `type` and `payload` is ignored. The generated output pkScript is returned in
181+
// BTCSignNextResponse. `contains_silent_payment_outputs` in the init request must be true.
182+
SilentPayment silent_payment = 8;
172183
}
173184

174185
message BTCScriptConfigRegistration {

py/bitbox02/bitbox02/communication/generated/btc_pb2.py

Lines changed: 50 additions & 48 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

py/bitbox02/bitbox02/communication/generated/btc_pb2.pyi

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ class BTCSignInitRequest(google.protobuf.message.Message):
292292
NUM_OUTPUTS_FIELD_NUMBER: builtins.int
293293
LOCKTIME_FIELD_NUMBER: builtins.int
294294
FORMAT_UNIT_FIELD_NUMBER: builtins.int
295+
CONTAINS_SILENT_PAYMENT_OUTPUTS_FIELD_NUMBER: builtins.int
295296
coin: global___BTCCoin.ValueType
296297
@property
297298
def script_configs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___BTCScriptConfigWithKeypath]:
@@ -306,6 +307,7 @@ class BTCSignInitRequest(google.protobuf.message.Message):
306307
"""must be <500000000"""
307308

308309
format_unit: global___BTCSignInitRequest.FormatUnit.ValueType
310+
contains_silent_payment_outputs: builtins.bool
309311
def __init__(self,
310312
*,
311313
coin: global___BTCCoin.ValueType = ...,
@@ -315,8 +317,9 @@ class BTCSignInitRequest(google.protobuf.message.Message):
315317
num_outputs: builtins.int = ...,
316318
locktime: builtins.int = ...,
317319
format_unit: global___BTCSignInitRequest.FormatUnit.ValueType = ...,
320+
contains_silent_payment_outputs: builtins.bool = ...,
318321
) -> None: ...
319-
def ClearField(self, field_name: typing_extensions.Literal["coin",b"coin","format_unit",b"format_unit","locktime",b"locktime","num_inputs",b"num_inputs","num_outputs",b"num_outputs","script_configs",b"script_configs","version",b"version"]) -> None: ...
322+
def ClearField(self, field_name: typing_extensions.Literal["coin",b"coin","contains_silent_payment_outputs",b"contains_silent_payment_outputs","format_unit",b"format_unit","locktime",b"locktime","num_inputs",b"num_inputs","num_outputs",b"num_outputs","script_configs",b"script_configs","version",b"version"]) -> None: ...
320323
global___BTCSignInitRequest = BTCSignInitRequest
321324

322325
class BTCSignNextResponse(google.protobuf.message.Message):
@@ -356,6 +359,8 @@ class BTCSignNextResponse(google.protobuf.message.Message):
356359
SIGNATURE_FIELD_NUMBER: builtins.int
357360
PREV_INDEX_FIELD_NUMBER: builtins.int
358361
ANTI_KLEPTO_SIGNER_COMMITMENT_FIELD_NUMBER: builtins.int
362+
GENERATED_OUTPUT_PKSCRIPT_FIELD_NUMBER: builtins.int
363+
SILENT_PAYMENT_DLEQ_PROOF_FIELD_NUMBER: builtins.int
359364
type: global___BTCSignNextResponse.Type.ValueType
360365
index: builtins.int
361366
"""index of the current input or output"""
@@ -371,6 +376,10 @@ class BTCSignNextResponse(google.protobuf.message.Message):
371376

372377
@property
373378
def anti_klepto_signer_commitment(self) -> antiklepto_pb2.AntiKleptoSignerCommitment: ...
379+
generated_output_pkscript: builtins.bytes
380+
"""Generated output. The host *must* verify its correctness using `silent_payment_dleq_proof`."""
381+
382+
silent_payment_dleq_proof: builtins.bytes
374383
def __init__(self,
375384
*,
376385
type: global___BTCSignNextResponse.Type.ValueType = ...,
@@ -379,9 +388,11 @@ class BTCSignNextResponse(google.protobuf.message.Message):
379388
signature: builtins.bytes = ...,
380389
prev_index: builtins.int = ...,
381390
anti_klepto_signer_commitment: typing.Optional[antiklepto_pb2.AntiKleptoSignerCommitment] = ...,
391+
generated_output_pkscript: builtins.bytes = ...,
392+
silent_payment_dleq_proof: builtins.bytes = ...,
382393
) -> None: ...
383394
def HasField(self, field_name: typing_extensions.Literal["anti_klepto_signer_commitment",b"anti_klepto_signer_commitment"]) -> builtins.bool: ...
384-
def ClearField(self, field_name: typing_extensions.Literal["anti_klepto_signer_commitment",b"anti_klepto_signer_commitment","has_signature",b"has_signature","index",b"index","prev_index",b"prev_index","signature",b"signature","type",b"type"]) -> None: ...
395+
def ClearField(self, field_name: typing_extensions.Literal["anti_klepto_signer_commitment",b"anti_klepto_signer_commitment","generated_output_pkscript",b"generated_output_pkscript","has_signature",b"has_signature","index",b"index","prev_index",b"prev_index","signature",b"signature","silent_payment_dleq_proof",b"silent_payment_dleq_proof","type",b"type"]) -> None: ...
385396
global___BTCSignNextResponse = BTCSignNextResponse
386397

387398
class BTCSignInputRequest(google.protobuf.message.Message):
@@ -424,13 +435,25 @@ global___BTCSignInputRequest = BTCSignInputRequest
424435

425436
class BTCSignOutputRequest(google.protobuf.message.Message):
426437
DESCRIPTOR: google.protobuf.descriptor.Descriptor
438+
class SilentPayment(google.protobuf.message.Message):
439+
"""https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki"""
440+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
441+
ADDRESS_FIELD_NUMBER: builtins.int
442+
address: typing.Text
443+
def __init__(self,
444+
*,
445+
address: typing.Text = ...,
446+
) -> None: ...
447+
def ClearField(self, field_name: typing_extensions.Literal["address",b"address"]) -> None: ...
448+
427449
OURS_FIELD_NUMBER: builtins.int
428450
TYPE_FIELD_NUMBER: builtins.int
429451
VALUE_FIELD_NUMBER: builtins.int
430452
PAYLOAD_FIELD_NUMBER: builtins.int
431453
KEYPATH_FIELD_NUMBER: builtins.int
432454
SCRIPT_CONFIG_INDEX_FIELD_NUMBER: builtins.int
433455
PAYMENT_REQUEST_INDEX_FIELD_NUMBER: builtins.int
456+
SILENT_PAYMENT_FIELD_NUMBER: builtins.int
434457
ours: builtins.bool
435458
type: global___BTCOutputType.ValueType
436459
"""if ours is false"""
@@ -449,6 +472,12 @@ class BTCSignOutputRequest(google.protobuf.message.Message):
449472
"""If ours is true. References a script config from BTCSignInitRequest"""
450473

451474
payment_request_index: builtins.int
475+
@property
476+
def silent_payment(self) -> global___BTCSignOutputRequest.SilentPayment:
477+
"""If provided, `type` and `payload` is ignored. The generated output pkScript is returned in
478+
BTCSignNextResponse. `contains_silent_payment_outputs` in the init request must be true.
479+
"""
480+
pass
452481
def __init__(self,
453482
*,
454483
ours: builtins.bool = ...,
@@ -458,9 +487,10 @@ class BTCSignOutputRequest(google.protobuf.message.Message):
458487
keypath: typing.Optional[typing.Iterable[builtins.int]] = ...,
459488
script_config_index: builtins.int = ...,
460489
payment_request_index: typing.Optional[builtins.int] = ...,
490+
silent_payment: typing.Optional[global___BTCSignOutputRequest.SilentPayment] = ...,
461491
) -> None: ...
462-
def HasField(self, field_name: typing_extensions.Literal["_payment_request_index",b"_payment_request_index","payment_request_index",b"payment_request_index"]) -> builtins.bool: ...
463-
def ClearField(self, field_name: typing_extensions.Literal["_payment_request_index",b"_payment_request_index","keypath",b"keypath","ours",b"ours","payload",b"payload","payment_request_index",b"payment_request_index","script_config_index",b"script_config_index","type",b"type","value",b"value"]) -> None: ...
492+
def HasField(self, field_name: typing_extensions.Literal["_payment_request_index",b"_payment_request_index","payment_request_index",b"payment_request_index","silent_payment",b"silent_payment"]) -> builtins.bool: ...
493+
def ClearField(self, field_name: typing_extensions.Literal["_payment_request_index",b"_payment_request_index","keypath",b"keypath","ours",b"ours","payload",b"payload","payment_request_index",b"payment_request_index","script_config_index",b"script_config_index","silent_payment",b"silent_payment","type",b"type","value",b"value"]) -> None: ...
464494
def WhichOneof(self, oneof_group: typing_extensions.Literal["_payment_request_index",b"_payment_request_index"]) -> typing.Optional[typing_extensions.Literal["payment_request_index"]]: ...
465495
global___BTCSignOutputRequest = BTCSignOutputRequest
466496

src/keystore.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,29 @@ bool keystore_secp256k1_schnorr_bip86_sign(
10001000
return secp256k1_schnorrsig_verify(ctx, sig64_out, msg32, 32, &pubkey) == 1;
10011001
}
10021002

1003+
bool keystore_secp256k1_get_private_key(
1004+
const uint32_t* keypath,
1005+
const size_t keypath_len,
1006+
bool tweak_bip86,
1007+
uint8_t* key_out)
1008+
{
1009+
if (tweak_bip86) {
1010+
secp256k1_keypair __attribute__((__cleanup__(_cleanup_keypair))) keypair = {0};
1011+
secp256k1_xonly_pubkey pubkey = {0};
1012+
if (!_schnorr_bip86_keypair(keypath, keypath_len, &keypair, &pubkey)) {
1013+
return false;
1014+
}
1015+
const secp256k1_context* ctx = wally_get_secp_context();
1016+
return secp256k1_keypair_sec(ctx, key_out, &keypair) == 1;
1017+
}
1018+
struct ext_key xprv __attribute__((__cleanup__(keystore_zero_xkey))) = {0};
1019+
if (!_get_xprv_twice(keypath, keypath_len, &xprv)) {
1020+
return false;
1021+
}
1022+
memcpy(key_out, xprv.priv_key + 1, 32);
1023+
return true;
1024+
}
1025+
10031026
#ifdef TESTING
10041027
void keystore_mock_unlocked(const uint8_t* seed, size_t seed_len, const uint8_t* bip39_seed)
10051028
{

src/keystore.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,20 @@ USE_RESULT bool keystore_secp256k1_schnorr_bip86_sign(
303303
const uint8_t* msg32,
304304
uint8_t* sig64_out);
305305

306+
/**
307+
* Get the private key at the keypath.
308+
*
309+
* @param[in] keypath derivation keypath
310+
* @param[in] keypath_len number of elements in keypath
311+
* @param[in] tweak_bip86 if true, the resulting private key is tweaked with the BIP-86 tweak.
312+
* @param[out] key_out resulting private key, must be 32 bytes.
313+
*/
314+
USE_RESULT bool keystore_secp256k1_get_private_key(
315+
const uint32_t* keypath,
316+
size_t keypath_len,
317+
bool tweak_bip86,
318+
uint8_t* key_out);
319+
306320
#ifdef TESTING
307321
/**
308322
* convenience to mock the keystore state (locked, seed) in tests.

src/rust/Cargo.lock

Lines changed: 58 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/rust/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ members = [
2222
"bitbox02",
2323
"bitbox02-sys",
2424
"erc20_params",
25+
"streaming-silent-payments",
2526
]
2627

2728
resolver = "2"
2829

2930
[workspace.dependencies]
31+
bech32 = { version = "0.11.0", default-features = false }
3032
bitcoin = { version = "0.32.2", default-features = false }
3133
hex = { version = "0.4", default-features = false, features = ["alloc"] }
3234
num-bigint = { version = "0.4.3", default-features = false }

src/rust/bitbox02-rust/Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ util = { path = "../util" }
3232
erc20_params = { path = "../erc20_params", optional = true }
3333
binascii = { version = "0.1.4", default-features = false, features = ["encode"] }
3434
bitbox02-noise = {path = "../bitbox02-noise"}
35+
streaming-silent-payments = { path = "../streaming-silent-payments", optional = true }
3536
hex = { workspace = true }
3637
sha2 = { workspace = true }
3738
sha3 = { workspace = true, optional = true }
@@ -41,8 +42,8 @@ num-bigint = { workspace = true, optional = true }
4142
num-traits = { version = "0.2", default-features = false }
4243
# If you change this, also change src/rust/.cargo/config.toml.
4344
bip32-ed25519 = { git = "https://github.com/BitBoxSwiss/rust-bip32-ed25519", tag = "v0.2.0", optional = true }
44-
bech32 = { version = "0.11.0", default-features = false, features = ["alloc"], optional = true }
45-
blake2 = { version = "0.10.6", default-features = false, features = ["size_opt"], optional = true }
45+
bech32 = { workspace = true, optional = true }
46+
blake2 = { version = "0.10.6", default-features = false, optional = true }
4647
minicbor = { version = "0.24.0", default-features = false, features = ["alloc"], optional = true }
4748
crc = { version = "3.0.1", optional = true }
4849
ed25519-dalek = { version = "2.1.1", default-features = false, features = ["hazmat", "digest"], optional = true }
@@ -77,6 +78,7 @@ app-ethereum = [
7778
app-bitcoin = [
7879
"dep:bech32",
7980
"dep:miniscript",
81+
"dep:streaming-silent-payments",
8082
"bitbox02/app-bitcoin",
8183
]
8284
app-litecoin = [

0 commit comments

Comments
 (0)