diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index f2d3cfa881..9be2bb6315 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -17,7 +17,7 @@ string(REPLACE "-mfpu=fpv4-sp-d16" "" MODIFIED_C_FLAGS ${MODIFIED_C_FLAGS_TMP}) # wally-core # configure flags for secp256k1 bundled in libwally core, to reduce memory consumption -set(LIBWALLY_SECP256k1_FLAGS --with-ecmult-window=2 --with-ecmult-gen-precision=2 --enable-ecmult-static-precomputation --enable-module-schnorrsig) +set(LIBWALLY_SECP256k1_FLAGS --with-ecmult-window=2 --with-ecmult-gen-precision=2 --enable-ecmult-static-precomputation --enable-module-schnorrsig --enable-module-ecdsa-adaptor) set(LIBWALLY_CONFIGURE_FLAGS --enable-static --disable-shared --disable-tests ${LIBWALLY_SECP256k1_FLAGS}) if(SANITIZE_ADDRESS) set(LIBWALLY_CFLAGS "-fsanitize=address") diff --git a/messages/btc.proto b/messages/btc.proto index 7a061f9518..370d824a67 100644 --- a/messages/btc.proto +++ b/messages/btc.proto @@ -137,6 +137,7 @@ message BTCSignNextResponse { uint32 prev_index = 5; AntiKleptoSignerCommitment anti_klepto_signer_commitment = 6; bytes generated_output_pkscript = 7; + bytes silent_payment_dleq_proof = 8; } message BTCSignInputRequest { @@ -173,8 +174,8 @@ message BTCSignOutputRequest { // If ours is true. References a script config from BTCSignInitRequest uint32 script_config_index = 6; optional uint32 payment_request_index = 7; - // If provided, `type` must be `P2TR` and the payload must be empty. The output's pkScript is - // returned BTCSignNextResponse. + // If provided, `type` and `payload` is ignored. The generated output pkScript is returned in + // BTCSignNextResponse. SilentPayment silent_payment = 8; } diff --git a/py/bitbox02/bitbox02/communication/generated/btc_pb2.py b/py/bitbox02/bitbox02/communication/generated/btc_pb2.py index 32c94f1485..a01f2c1f26 100644 --- a/py/bitbox02/bitbox02/communication/generated/btc_pb2.py +++ b/py/bitbox02/bitbox02/communication/generated/btc_pb2.py @@ -15,17 +15,17 @@ from . import antiklepto_pb2 as antiklepto__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tbtc.proto\x12\x14shiftcrypto.bitbox02\x1a\x0c\x63ommon.proto\x1a\x10\x61ntiklepto.proto\"\xc6\x04\n\x0f\x42TCScriptConfig\x12G\n\x0bsimple_type\x18\x01 \x01(\x0e\x32\x30.shiftcrypto.bitbox02.BTCScriptConfig.SimpleTypeH\x00\x12\x42\n\x08multisig\x18\x02 \x01(\x0b\x32..shiftcrypto.bitbox02.BTCScriptConfig.MultisigH\x00\x12>\n\x06policy\x18\x03 \x01(\x0b\x32,.shiftcrypto.bitbox02.BTCScriptConfig.PolicyH\x00\x1a\xd9\x01\n\x08Multisig\x12\x11\n\tthreshold\x18\x01 \x01(\r\x12)\n\x05xpubs\x18\x02 \x03(\x0b\x32\x1a.shiftcrypto.bitbox02.XPub\x12\x16\n\x0eour_xpub_index\x18\x03 \x01(\r\x12N\n\x0bscript_type\x18\x04 \x01(\x0e\x32\x39.shiftcrypto.bitbox02.BTCScriptConfig.Multisig.ScriptType\"\'\n\nScriptType\x12\t\n\x05P2WSH\x10\x00\x12\x0e\n\nP2WSH_P2SH\x10\x01\x1aK\n\x06Policy\x12\x0e\n\x06policy\x18\x01 \x01(\t\x12\x31\n\x04keys\x18\x02 \x03(\x0b\x32#.shiftcrypto.bitbox02.KeyOriginInfo\"3\n\nSimpleType\x12\x0f\n\x0bP2WPKH_P2SH\x10\x00\x12\n\n\x06P2WPKH\x10\x01\x12\x08\n\x04P2TR\x10\x02\x42\x08\n\x06\x63onfig\"\xfc\x02\n\rBTCPubRequest\x12+\n\x04\x63oin\x18\x01 \x01(\x0e\x32\x1d.shiftcrypto.bitbox02.BTCCoin\x12\x0f\n\x07keypath\x18\x02 \x03(\r\x12\x41\n\txpub_type\x18\x03 \x01(\x0e\x32,.shiftcrypto.bitbox02.BTCPubRequest.XPubTypeH\x00\x12>\n\rscript_config\x18\x04 \x01(\x0b\x32%.shiftcrypto.bitbox02.BTCScriptConfigH\x00\x12\x0f\n\x07\x64isplay\x18\x05 \x01(\x08\"\x8e\x01\n\x08XPubType\x12\x08\n\x04TPUB\x10\x00\x12\x08\n\x04XPUB\x10\x01\x12\x08\n\x04YPUB\x10\x02\x12\x08\n\x04ZPUB\x10\x03\x12\x08\n\x04VPUB\x10\x04\x12\x08\n\x04UPUB\x10\x05\x12\x10\n\x0c\x43\x41PITAL_VPUB\x10\x06\x12\x10\n\x0c\x43\x41PITAL_ZPUB\x10\x07\x12\x10\n\x0c\x43\x41PITAL_UPUB\x10\x08\x12\x10\n\x0c\x43\x41PITAL_YPUB\x10\tB\x08\n\x06output\"k\n\x1a\x42TCScriptConfigWithKeypath\x12<\n\rscript_config\x18\x02 \x01(\x0b\x32%.shiftcrypto.bitbox02.BTCScriptConfig\x12\x0f\n\x07keypath\x18\x03 \x03(\r\"\xee\x02\n\x12\x42TCSignInitRequest\x12+\n\x04\x63oin\x18\x01 \x01(\x0e\x32\x1d.shiftcrypto.bitbox02.BTCCoin\x12H\n\x0escript_configs\x18\x02 \x03(\x0b\x32\x30.shiftcrypto.bitbox02.BTCScriptConfigWithKeypath\x12\x0f\n\x07version\x18\x04 \x01(\r\x12\x12\n\nnum_inputs\x18\x05 \x01(\r\x12\x13\n\x0bnum_outputs\x18\x06 \x01(\r\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12H\n\x0b\x66ormat_unit\x18\x08 \x01(\x0e\x32\x33.shiftcrypto.bitbox02.BTCSignInitRequest.FormatUnit\x12\'\n\x1f\x63ontains_silent_payment_outputs\x18\t \x01(\x08\"\"\n\nFormatUnit\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x00\x12\x07\n\x03SAT\x10\x01\"\xa1\x03\n\x13\x42TCSignNextResponse\x12<\n\x04type\x18\x01 \x01(\x0e\x32..shiftcrypto.bitbox02.BTCSignNextResponse.Type\x12\r\n\x05index\x18\x02 \x01(\r\x12\x15\n\rhas_signature\x18\x03 \x01(\x08\x12\x11\n\tsignature\x18\x04 \x01(\x0c\x12\x12\n\nprev_index\x18\x05 \x01(\r\x12W\n\x1d\x61nti_klepto_signer_commitment\x18\x06 \x01(\x0b\x32\x30.shiftcrypto.bitbox02.AntiKleptoSignerCommitment\x12!\n\x19generated_output_pkscript\x18\x07 \x01(\x0c\"\x82\x01\n\x04Type\x12\t\n\x05INPUT\x10\x00\x12\n\n\x06OUTPUT\x10\x01\x12\x08\n\x04\x44ONE\x10\x02\x12\x0f\n\x0bPREVTX_INIT\x10\x03\x12\x10\n\x0cPREVTX_INPUT\x10\x04\x12\x11\n\rPREVTX_OUTPUT\x10\x05\x12\x0e\n\nHOST_NONCE\x10\x06\x12\x13\n\x0fPAYMENT_REQUEST\x10\x07\"\xea\x01\n\x13\x42TCSignInputRequest\x12\x13\n\x0bprevOutHash\x18\x01 \x01(\x0c\x12\x14\n\x0cprevOutIndex\x18\x02 \x01(\r\x12\x14\n\x0cprevOutValue\x18\x03 \x01(\x04\x12\x10\n\x08sequence\x18\x04 \x01(\r\x12\x0f\n\x07keypath\x18\x06 \x03(\r\x12\x1b\n\x13script_config_index\x18\x07 \x01(\r\x12R\n\x15host_nonce_commitment\x18\x08 \x01(\x0b\x32\x33.shiftcrypto.bitbox02.AntiKleptoHostNonceCommitment\"\xd7\x02\n\x14\x42TCSignOutputRequest\x12\x0c\n\x04ours\x18\x01 \x01(\x08\x12\x31\n\x04type\x18\x02 \x01(\x0e\x32#.shiftcrypto.bitbox02.BTCOutputType\x12\r\n\x05value\x18\x03 \x01(\x04\x12\x0f\n\x07payload\x18\x04 \x01(\x0c\x12\x0f\n\x07keypath\x18\x05 \x03(\r\x12\x1b\n\x13script_config_index\x18\x06 \x01(\r\x12\"\n\x15payment_request_index\x18\x07 \x01(\rH\x00\x88\x01\x01\x12P\n\x0esilent_payment\x18\x08 \x01(\x0b\x32\x38.shiftcrypto.bitbox02.BTCSignOutputRequest.SilentPayment\x1a \n\rSilentPayment\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\tB\x18\n\x16_payment_request_index\"\x99\x01\n\x1b\x42TCScriptConfigRegistration\x12+\n\x04\x63oin\x18\x01 \x01(\x0e\x32\x1d.shiftcrypto.bitbox02.BTCCoin\x12<\n\rscript_config\x18\x02 \x01(\x0b\x32%.shiftcrypto.bitbox02.BTCScriptConfig\x12\x0f\n\x07keypath\x18\x03 \x03(\r\"\x0c\n\nBTCSuccess\"m\n\"BTCIsScriptConfigRegisteredRequest\x12G\n\x0cregistration\x18\x01 \x01(\x0b\x32\x31.shiftcrypto.bitbox02.BTCScriptConfigRegistration\"<\n#BTCIsScriptConfigRegisteredResponse\x12\x15\n\ris_registered\x18\x01 \x01(\x08\"\xfc\x01\n\x1e\x42TCRegisterScriptConfigRequest\x12G\n\x0cregistration\x18\x01 \x01(\x0b\x32\x31.shiftcrypto.bitbox02.BTCScriptConfigRegistration\x12\x0c\n\x04name\x18\x02 \x01(\t\x12P\n\txpub_type\x18\x03 \x01(\x0e\x32=.shiftcrypto.bitbox02.BTCRegisterScriptConfigRequest.XPubType\"1\n\x08XPubType\x12\x11\n\rAUTO_ELECTRUM\x10\x00\x12\x12\n\x0e\x41UTO_XPUB_TPUB\x10\x01\"b\n\x14\x42TCPrevTxInitRequest\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x12\n\nnum_inputs\x18\x02 \x01(\r\x12\x13\n\x0bnum_outputs\x18\x03 \x01(\r\x12\x10\n\x08locktime\x18\x04 \x01(\r\"r\n\x15\x42TCPrevTxInputRequest\x12\x15\n\rprev_out_hash\x18\x01 \x01(\x0c\x12\x16\n\x0eprev_out_index\x18\x02 \x01(\r\x12\x18\n\x10signature_script\x18\x03 \x01(\x0c\x12\x10\n\x08sequence\x18\x04 \x01(\r\">\n\x16\x42TCPrevTxOutputRequest\x12\r\n\x05value\x18\x01 \x01(\x04\x12\x15\n\rpubkey_script\x18\x02 \x01(\x0c\"\xab\x02\n\x18\x42TCPaymentRequestRequest\x12\x16\n\x0erecipient_name\x18\x01 \x01(\t\x12\x42\n\x05memos\x18\x02 \x03(\x0b\x32\x33.shiftcrypto.bitbox02.BTCPaymentRequestRequest.Memo\x12\r\n\x05nonce\x18\x03 \x01(\x0c\x12\x14\n\x0ctotal_amount\x18\x04 \x01(\x04\x12\x11\n\tsignature\x18\x05 \x01(\x0c\x1a{\n\x04Memo\x12Q\n\ttext_memo\x18\x01 \x01(\x0b\x32<.shiftcrypto.bitbox02.BTCPaymentRequestRequest.Memo.TextMemoH\x00\x1a\x18\n\x08TextMemo\x12\x0c\n\x04note\x18\x01 \x01(\tB\x06\n\x04memo\"\xee\x01\n\x15\x42TCSignMessageRequest\x12+\n\x04\x63oin\x18\x01 \x01(\x0e\x32\x1d.shiftcrypto.bitbox02.BTCCoin\x12G\n\rscript_config\x18\x02 \x01(\x0b\x32\x30.shiftcrypto.bitbox02.BTCScriptConfigWithKeypath\x12\x0b\n\x03msg\x18\x03 \x01(\x0c\x12R\n\x15host_nonce_commitment\x18\x04 \x01(\x0b\x32\x33.shiftcrypto.bitbox02.AntiKleptoHostNonceCommitment\"+\n\x16\x42TCSignMessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\"\x81\x05\n\nBTCRequest\x12_\n\x1bis_script_config_registered\x18\x01 \x01(\x0b\x32\x38.shiftcrypto.bitbox02.BTCIsScriptConfigRegisteredRequestH\x00\x12V\n\x16register_script_config\x18\x02 \x01(\x0b\x32\x34.shiftcrypto.bitbox02.BTCRegisterScriptConfigRequestH\x00\x12\x41\n\x0bprevtx_init\x18\x03 \x01(\x0b\x32*.shiftcrypto.bitbox02.BTCPrevTxInitRequestH\x00\x12\x43\n\x0cprevtx_input\x18\x04 \x01(\x0b\x32+.shiftcrypto.bitbox02.BTCPrevTxInputRequestH\x00\x12\x45\n\rprevtx_output\x18\x05 \x01(\x0b\x32,.shiftcrypto.bitbox02.BTCPrevTxOutputRequestH\x00\x12\x43\n\x0csign_message\x18\x06 \x01(\x0b\x32+.shiftcrypto.bitbox02.BTCSignMessageRequestH\x00\x12P\n\x14\x61ntiklepto_signature\x18\x07 \x01(\x0b\x32\x30.shiftcrypto.bitbox02.AntiKleptoSignatureRequestH\x00\x12I\n\x0fpayment_request\x18\x08 \x01(\x0b\x32..shiftcrypto.bitbox02.BTCPaymentRequestRequestH\x00\x42\t\n\x07request\"\x90\x03\n\x0b\x42TCResponse\x12\x33\n\x07success\x18\x01 \x01(\x0b\x32 .shiftcrypto.bitbox02.BTCSuccessH\x00\x12`\n\x1bis_script_config_registered\x18\x02 \x01(\x0b\x32\x39.shiftcrypto.bitbox02.BTCIsScriptConfigRegisteredResponseH\x00\x12>\n\tsign_next\x18\x03 \x01(\x0b\x32).shiftcrypto.bitbox02.BTCSignNextResponseH\x00\x12\x44\n\x0csign_message\x18\x04 \x01(\x0b\x32,.shiftcrypto.bitbox02.BTCSignMessageResponseH\x00\x12X\n\x1c\x61ntiklepto_signer_commitment\x18\x05 \x01(\x0b\x32\x30.shiftcrypto.bitbox02.AntiKleptoSignerCommitmentH\x00\x42\n\n\x08response*/\n\x07\x42TCCoin\x12\x07\n\x03\x42TC\x10\x00\x12\x08\n\x04TBTC\x10\x01\x12\x07\n\x03LTC\x10\x02\x12\x08\n\x04TLTC\x10\x03*R\n\rBTCOutputType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\t\n\x05P2PKH\x10\x01\x12\x08\n\x04P2SH\x10\x02\x12\n\n\x06P2WPKH\x10\x03\x12\t\n\x05P2WSH\x10\x04\x12\x08\n\x04P2TR\x10\x05\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tbtc.proto\x12\x14shiftcrypto.bitbox02\x1a\x0c\x63ommon.proto\x1a\x10\x61ntiklepto.proto\"\xc6\x04\n\x0f\x42TCScriptConfig\x12G\n\x0bsimple_type\x18\x01 \x01(\x0e\x32\x30.shiftcrypto.bitbox02.BTCScriptConfig.SimpleTypeH\x00\x12\x42\n\x08multisig\x18\x02 \x01(\x0b\x32..shiftcrypto.bitbox02.BTCScriptConfig.MultisigH\x00\x12>\n\x06policy\x18\x03 \x01(\x0b\x32,.shiftcrypto.bitbox02.BTCScriptConfig.PolicyH\x00\x1a\xd9\x01\n\x08Multisig\x12\x11\n\tthreshold\x18\x01 \x01(\r\x12)\n\x05xpubs\x18\x02 \x03(\x0b\x32\x1a.shiftcrypto.bitbox02.XPub\x12\x16\n\x0eour_xpub_index\x18\x03 \x01(\r\x12N\n\x0bscript_type\x18\x04 \x01(\x0e\x32\x39.shiftcrypto.bitbox02.BTCScriptConfig.Multisig.ScriptType\"\'\n\nScriptType\x12\t\n\x05P2WSH\x10\x00\x12\x0e\n\nP2WSH_P2SH\x10\x01\x1aK\n\x06Policy\x12\x0e\n\x06policy\x18\x01 \x01(\t\x12\x31\n\x04keys\x18\x02 \x03(\x0b\x32#.shiftcrypto.bitbox02.KeyOriginInfo\"3\n\nSimpleType\x12\x0f\n\x0bP2WPKH_P2SH\x10\x00\x12\n\n\x06P2WPKH\x10\x01\x12\x08\n\x04P2TR\x10\x02\x42\x08\n\x06\x63onfig\"\xfc\x02\n\rBTCPubRequest\x12+\n\x04\x63oin\x18\x01 \x01(\x0e\x32\x1d.shiftcrypto.bitbox02.BTCCoin\x12\x0f\n\x07keypath\x18\x02 \x03(\r\x12\x41\n\txpub_type\x18\x03 \x01(\x0e\x32,.shiftcrypto.bitbox02.BTCPubRequest.XPubTypeH\x00\x12>\n\rscript_config\x18\x04 \x01(\x0b\x32%.shiftcrypto.bitbox02.BTCScriptConfigH\x00\x12\x0f\n\x07\x64isplay\x18\x05 \x01(\x08\"\x8e\x01\n\x08XPubType\x12\x08\n\x04TPUB\x10\x00\x12\x08\n\x04XPUB\x10\x01\x12\x08\n\x04YPUB\x10\x02\x12\x08\n\x04ZPUB\x10\x03\x12\x08\n\x04VPUB\x10\x04\x12\x08\n\x04UPUB\x10\x05\x12\x10\n\x0c\x43\x41PITAL_VPUB\x10\x06\x12\x10\n\x0c\x43\x41PITAL_ZPUB\x10\x07\x12\x10\n\x0c\x43\x41PITAL_UPUB\x10\x08\x12\x10\n\x0c\x43\x41PITAL_YPUB\x10\tB\x08\n\x06output\"k\n\x1a\x42TCScriptConfigWithKeypath\x12<\n\rscript_config\x18\x02 \x01(\x0b\x32%.shiftcrypto.bitbox02.BTCScriptConfig\x12\x0f\n\x07keypath\x18\x03 \x03(\r\"\xee\x02\n\x12\x42TCSignInitRequest\x12+\n\x04\x63oin\x18\x01 \x01(\x0e\x32\x1d.shiftcrypto.bitbox02.BTCCoin\x12H\n\x0escript_configs\x18\x02 \x03(\x0b\x32\x30.shiftcrypto.bitbox02.BTCScriptConfigWithKeypath\x12\x0f\n\x07version\x18\x04 \x01(\r\x12\x12\n\nnum_inputs\x18\x05 \x01(\r\x12\x13\n\x0bnum_outputs\x18\x06 \x01(\r\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12H\n\x0b\x66ormat_unit\x18\x08 \x01(\x0e\x32\x33.shiftcrypto.bitbox02.BTCSignInitRequest.FormatUnit\x12\'\n\x1f\x63ontains_silent_payment_outputs\x18\t \x01(\x08\"\"\n\nFormatUnit\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x00\x12\x07\n\x03SAT\x10\x01\"\xc4\x03\n\x13\x42TCSignNextResponse\x12<\n\x04type\x18\x01 \x01(\x0e\x32..shiftcrypto.bitbox02.BTCSignNextResponse.Type\x12\r\n\x05index\x18\x02 \x01(\r\x12\x15\n\rhas_signature\x18\x03 \x01(\x08\x12\x11\n\tsignature\x18\x04 \x01(\x0c\x12\x12\n\nprev_index\x18\x05 \x01(\r\x12W\n\x1d\x61nti_klepto_signer_commitment\x18\x06 \x01(\x0b\x32\x30.shiftcrypto.bitbox02.AntiKleptoSignerCommitment\x12!\n\x19generated_output_pkscript\x18\x07 \x01(\x0c\x12!\n\x19silent_payment_dleq_proof\x18\x08 \x01(\x0c\"\x82\x01\n\x04Type\x12\t\n\x05INPUT\x10\x00\x12\n\n\x06OUTPUT\x10\x01\x12\x08\n\x04\x44ONE\x10\x02\x12\x0f\n\x0bPREVTX_INIT\x10\x03\x12\x10\n\x0cPREVTX_INPUT\x10\x04\x12\x11\n\rPREVTX_OUTPUT\x10\x05\x12\x0e\n\nHOST_NONCE\x10\x06\x12\x13\n\x0fPAYMENT_REQUEST\x10\x07\"\xea\x01\n\x13\x42TCSignInputRequest\x12\x13\n\x0bprevOutHash\x18\x01 \x01(\x0c\x12\x14\n\x0cprevOutIndex\x18\x02 \x01(\r\x12\x14\n\x0cprevOutValue\x18\x03 \x01(\x04\x12\x10\n\x08sequence\x18\x04 \x01(\r\x12\x0f\n\x07keypath\x18\x06 \x03(\r\x12\x1b\n\x13script_config_index\x18\x07 \x01(\r\x12R\n\x15host_nonce_commitment\x18\x08 \x01(\x0b\x32\x33.shiftcrypto.bitbox02.AntiKleptoHostNonceCommitment\"\xd7\x02\n\x14\x42TCSignOutputRequest\x12\x0c\n\x04ours\x18\x01 \x01(\x08\x12\x31\n\x04type\x18\x02 \x01(\x0e\x32#.shiftcrypto.bitbox02.BTCOutputType\x12\r\n\x05value\x18\x03 \x01(\x04\x12\x0f\n\x07payload\x18\x04 \x01(\x0c\x12\x0f\n\x07keypath\x18\x05 \x03(\r\x12\x1b\n\x13script_config_index\x18\x06 \x01(\r\x12\"\n\x15payment_request_index\x18\x07 \x01(\rH\x00\x88\x01\x01\x12P\n\x0esilent_payment\x18\x08 \x01(\x0b\x32\x38.shiftcrypto.bitbox02.BTCSignOutputRequest.SilentPayment\x1a \n\rSilentPayment\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\tB\x18\n\x16_payment_request_index\"\x99\x01\n\x1b\x42TCScriptConfigRegistration\x12+\n\x04\x63oin\x18\x01 \x01(\x0e\x32\x1d.shiftcrypto.bitbox02.BTCCoin\x12<\n\rscript_config\x18\x02 \x01(\x0b\x32%.shiftcrypto.bitbox02.BTCScriptConfig\x12\x0f\n\x07keypath\x18\x03 \x03(\r\"\x0c\n\nBTCSuccess\"m\n\"BTCIsScriptConfigRegisteredRequest\x12G\n\x0cregistration\x18\x01 \x01(\x0b\x32\x31.shiftcrypto.bitbox02.BTCScriptConfigRegistration\"<\n#BTCIsScriptConfigRegisteredResponse\x12\x15\n\ris_registered\x18\x01 \x01(\x08\"\xfc\x01\n\x1e\x42TCRegisterScriptConfigRequest\x12G\n\x0cregistration\x18\x01 \x01(\x0b\x32\x31.shiftcrypto.bitbox02.BTCScriptConfigRegistration\x12\x0c\n\x04name\x18\x02 \x01(\t\x12P\n\txpub_type\x18\x03 \x01(\x0e\x32=.shiftcrypto.bitbox02.BTCRegisterScriptConfigRequest.XPubType\"1\n\x08XPubType\x12\x11\n\rAUTO_ELECTRUM\x10\x00\x12\x12\n\x0e\x41UTO_XPUB_TPUB\x10\x01\"b\n\x14\x42TCPrevTxInitRequest\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x12\n\nnum_inputs\x18\x02 \x01(\r\x12\x13\n\x0bnum_outputs\x18\x03 \x01(\r\x12\x10\n\x08locktime\x18\x04 \x01(\r\"r\n\x15\x42TCPrevTxInputRequest\x12\x15\n\rprev_out_hash\x18\x01 \x01(\x0c\x12\x16\n\x0eprev_out_index\x18\x02 \x01(\r\x12\x18\n\x10signature_script\x18\x03 \x01(\x0c\x12\x10\n\x08sequence\x18\x04 \x01(\r\">\n\x16\x42TCPrevTxOutputRequest\x12\r\n\x05value\x18\x01 \x01(\x04\x12\x15\n\rpubkey_script\x18\x02 \x01(\x0c\"\xab\x02\n\x18\x42TCPaymentRequestRequest\x12\x16\n\x0erecipient_name\x18\x01 \x01(\t\x12\x42\n\x05memos\x18\x02 \x03(\x0b\x32\x33.shiftcrypto.bitbox02.BTCPaymentRequestRequest.Memo\x12\r\n\x05nonce\x18\x03 \x01(\x0c\x12\x14\n\x0ctotal_amount\x18\x04 \x01(\x04\x12\x11\n\tsignature\x18\x05 \x01(\x0c\x1a{\n\x04Memo\x12Q\n\ttext_memo\x18\x01 \x01(\x0b\x32<.shiftcrypto.bitbox02.BTCPaymentRequestRequest.Memo.TextMemoH\x00\x1a\x18\n\x08TextMemo\x12\x0c\n\x04note\x18\x01 \x01(\tB\x06\n\x04memo\"\xee\x01\n\x15\x42TCSignMessageRequest\x12+\n\x04\x63oin\x18\x01 \x01(\x0e\x32\x1d.shiftcrypto.bitbox02.BTCCoin\x12G\n\rscript_config\x18\x02 \x01(\x0b\x32\x30.shiftcrypto.bitbox02.BTCScriptConfigWithKeypath\x12\x0b\n\x03msg\x18\x03 \x01(\x0c\x12R\n\x15host_nonce_commitment\x18\x04 \x01(\x0b\x32\x33.shiftcrypto.bitbox02.AntiKleptoHostNonceCommitment\"+\n\x16\x42TCSignMessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\"\x81\x05\n\nBTCRequest\x12_\n\x1bis_script_config_registered\x18\x01 \x01(\x0b\x32\x38.shiftcrypto.bitbox02.BTCIsScriptConfigRegisteredRequestH\x00\x12V\n\x16register_script_config\x18\x02 \x01(\x0b\x32\x34.shiftcrypto.bitbox02.BTCRegisterScriptConfigRequestH\x00\x12\x41\n\x0bprevtx_init\x18\x03 \x01(\x0b\x32*.shiftcrypto.bitbox02.BTCPrevTxInitRequestH\x00\x12\x43\n\x0cprevtx_input\x18\x04 \x01(\x0b\x32+.shiftcrypto.bitbox02.BTCPrevTxInputRequestH\x00\x12\x45\n\rprevtx_output\x18\x05 \x01(\x0b\x32,.shiftcrypto.bitbox02.BTCPrevTxOutputRequestH\x00\x12\x43\n\x0csign_message\x18\x06 \x01(\x0b\x32+.shiftcrypto.bitbox02.BTCSignMessageRequestH\x00\x12P\n\x14\x61ntiklepto_signature\x18\x07 \x01(\x0b\x32\x30.shiftcrypto.bitbox02.AntiKleptoSignatureRequestH\x00\x12I\n\x0fpayment_request\x18\x08 \x01(\x0b\x32..shiftcrypto.bitbox02.BTCPaymentRequestRequestH\x00\x42\t\n\x07request\"\x90\x03\n\x0b\x42TCResponse\x12\x33\n\x07success\x18\x01 \x01(\x0b\x32 .shiftcrypto.bitbox02.BTCSuccessH\x00\x12`\n\x1bis_script_config_registered\x18\x02 \x01(\x0b\x32\x39.shiftcrypto.bitbox02.BTCIsScriptConfigRegisteredResponseH\x00\x12>\n\tsign_next\x18\x03 \x01(\x0b\x32).shiftcrypto.bitbox02.BTCSignNextResponseH\x00\x12\x44\n\x0csign_message\x18\x04 \x01(\x0b\x32,.shiftcrypto.bitbox02.BTCSignMessageResponseH\x00\x12X\n\x1c\x61ntiklepto_signer_commitment\x18\x05 \x01(\x0b\x32\x30.shiftcrypto.bitbox02.AntiKleptoSignerCommitmentH\x00\x42\n\n\x08response*/\n\x07\x42TCCoin\x12\x07\n\x03\x42TC\x10\x00\x12\x08\n\x04TBTC\x10\x01\x12\x07\n\x03LTC\x10\x02\x12\x08\n\x04TLTC\x10\x03*R\n\rBTCOutputType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\t\n\x05P2PKH\x10\x01\x12\x08\n\x04P2SH\x10\x02\x12\n\n\x06P2WPKH\x10\x03\x12\t\n\x05P2WSH\x10\x04\x12\x08\n\x04P2TR\x10\x05\x62\x06proto3') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'btc_pb2', globals()) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None - _BTCCOIN._serialized_start=5029 - _BTCCOIN._serialized_end=5076 - _BTCOUTPUTTYPE._serialized_start=5078 - _BTCOUTPUTTYPE._serialized_end=5160 + _BTCCOIN._serialized_start=5064 + _BTCCOIN._serialized_end=5111 + _BTCOUTPUTTYPE._serialized_start=5113 + _BTCOUTPUTTYPE._serialized_end=5195 _BTCSCRIPTCONFIG._serialized_start=68 _BTCSCRIPTCONFIG._serialized_end=650 _BTCSCRIPTCONFIG_MULTISIG._serialized_start=293 @@ -47,45 +47,45 @@ _BTCSIGNINITREQUEST_FORMATUNIT._serialized_start=1477 _BTCSIGNINITREQUEST_FORMATUNIT._serialized_end=1511 _BTCSIGNNEXTRESPONSE._serialized_start=1514 - _BTCSIGNNEXTRESPONSE._serialized_end=1931 - _BTCSIGNNEXTRESPONSE_TYPE._serialized_start=1801 - _BTCSIGNNEXTRESPONSE_TYPE._serialized_end=1931 - _BTCSIGNINPUTREQUEST._serialized_start=1934 - _BTCSIGNINPUTREQUEST._serialized_end=2168 - _BTCSIGNOUTPUTREQUEST._serialized_start=2171 - _BTCSIGNOUTPUTREQUEST._serialized_end=2514 - _BTCSIGNOUTPUTREQUEST_SILENTPAYMENT._serialized_start=2456 - _BTCSIGNOUTPUTREQUEST_SILENTPAYMENT._serialized_end=2488 - _BTCSCRIPTCONFIGREGISTRATION._serialized_start=2517 - _BTCSCRIPTCONFIGREGISTRATION._serialized_end=2670 - _BTCSUCCESS._serialized_start=2672 - _BTCSUCCESS._serialized_end=2684 - _BTCISSCRIPTCONFIGREGISTEREDREQUEST._serialized_start=2686 - _BTCISSCRIPTCONFIGREGISTEREDREQUEST._serialized_end=2795 - _BTCISSCRIPTCONFIGREGISTEREDRESPONSE._serialized_start=2797 - _BTCISSCRIPTCONFIGREGISTEREDRESPONSE._serialized_end=2857 - _BTCREGISTERSCRIPTCONFIGREQUEST._serialized_start=2860 - _BTCREGISTERSCRIPTCONFIGREQUEST._serialized_end=3112 - _BTCREGISTERSCRIPTCONFIGREQUEST_XPUBTYPE._serialized_start=3063 - _BTCREGISTERSCRIPTCONFIGREQUEST_XPUBTYPE._serialized_end=3112 - _BTCPREVTXINITREQUEST._serialized_start=3114 - _BTCPREVTXINITREQUEST._serialized_end=3212 - _BTCPREVTXINPUTREQUEST._serialized_start=3214 - _BTCPREVTXINPUTREQUEST._serialized_end=3328 - _BTCPREVTXOUTPUTREQUEST._serialized_start=3330 - _BTCPREVTXOUTPUTREQUEST._serialized_end=3392 - _BTCPAYMENTREQUESTREQUEST._serialized_start=3395 - _BTCPAYMENTREQUESTREQUEST._serialized_end=3694 - _BTCPAYMENTREQUESTREQUEST_MEMO._serialized_start=3571 - _BTCPAYMENTREQUESTREQUEST_MEMO._serialized_end=3694 - _BTCPAYMENTREQUESTREQUEST_MEMO_TEXTMEMO._serialized_start=3662 - _BTCPAYMENTREQUESTREQUEST_MEMO_TEXTMEMO._serialized_end=3686 - _BTCSIGNMESSAGEREQUEST._serialized_start=3697 - _BTCSIGNMESSAGEREQUEST._serialized_end=3935 - _BTCSIGNMESSAGERESPONSE._serialized_start=3937 - _BTCSIGNMESSAGERESPONSE._serialized_end=3980 - _BTCREQUEST._serialized_start=3983 - _BTCREQUEST._serialized_end=4624 - _BTCRESPONSE._serialized_start=4627 - _BTCRESPONSE._serialized_end=5027 + _BTCSIGNNEXTRESPONSE._serialized_end=1966 + _BTCSIGNNEXTRESPONSE_TYPE._serialized_start=1836 + _BTCSIGNNEXTRESPONSE_TYPE._serialized_end=1966 + _BTCSIGNINPUTREQUEST._serialized_start=1969 + _BTCSIGNINPUTREQUEST._serialized_end=2203 + _BTCSIGNOUTPUTREQUEST._serialized_start=2206 + _BTCSIGNOUTPUTREQUEST._serialized_end=2549 + _BTCSIGNOUTPUTREQUEST_SILENTPAYMENT._serialized_start=2491 + _BTCSIGNOUTPUTREQUEST_SILENTPAYMENT._serialized_end=2523 + _BTCSCRIPTCONFIGREGISTRATION._serialized_start=2552 + _BTCSCRIPTCONFIGREGISTRATION._serialized_end=2705 + _BTCSUCCESS._serialized_start=2707 + _BTCSUCCESS._serialized_end=2719 + _BTCISSCRIPTCONFIGREGISTEREDREQUEST._serialized_start=2721 + _BTCISSCRIPTCONFIGREGISTEREDREQUEST._serialized_end=2830 + _BTCISSCRIPTCONFIGREGISTEREDRESPONSE._serialized_start=2832 + _BTCISSCRIPTCONFIGREGISTEREDRESPONSE._serialized_end=2892 + _BTCREGISTERSCRIPTCONFIGREQUEST._serialized_start=2895 + _BTCREGISTERSCRIPTCONFIGREQUEST._serialized_end=3147 + _BTCREGISTERSCRIPTCONFIGREQUEST_XPUBTYPE._serialized_start=3098 + _BTCREGISTERSCRIPTCONFIGREQUEST_XPUBTYPE._serialized_end=3147 + _BTCPREVTXINITREQUEST._serialized_start=3149 + _BTCPREVTXINITREQUEST._serialized_end=3247 + _BTCPREVTXINPUTREQUEST._serialized_start=3249 + _BTCPREVTXINPUTREQUEST._serialized_end=3363 + _BTCPREVTXOUTPUTREQUEST._serialized_start=3365 + _BTCPREVTXOUTPUTREQUEST._serialized_end=3427 + _BTCPAYMENTREQUESTREQUEST._serialized_start=3430 + _BTCPAYMENTREQUESTREQUEST._serialized_end=3729 + _BTCPAYMENTREQUESTREQUEST_MEMO._serialized_start=3606 + _BTCPAYMENTREQUESTREQUEST_MEMO._serialized_end=3729 + _BTCPAYMENTREQUESTREQUEST_MEMO_TEXTMEMO._serialized_start=3697 + _BTCPAYMENTREQUESTREQUEST_MEMO_TEXTMEMO._serialized_end=3721 + _BTCSIGNMESSAGEREQUEST._serialized_start=3732 + _BTCSIGNMESSAGEREQUEST._serialized_end=3970 + _BTCSIGNMESSAGERESPONSE._serialized_start=3972 + _BTCSIGNMESSAGERESPONSE._serialized_end=4015 + _BTCREQUEST._serialized_start=4018 + _BTCREQUEST._serialized_end=4659 + _BTCRESPONSE._serialized_start=4662 + _BTCRESPONSE._serialized_end=5062 # @@protoc_insertion_point(module_scope) diff --git a/py/bitbox02/bitbox02/communication/generated/btc_pb2.pyi b/py/bitbox02/bitbox02/communication/generated/btc_pb2.pyi index 2d95e8f2ae..07ae2c810e 100644 --- a/py/bitbox02/bitbox02/communication/generated/btc_pb2.pyi +++ b/py/bitbox02/bitbox02/communication/generated/btc_pb2.pyi @@ -354,6 +354,7 @@ class BTCSignNextResponse(google.protobuf.message.Message): PREV_INDEX_FIELD_NUMBER: builtins.int ANTI_KLEPTO_SIGNER_COMMITMENT_FIELD_NUMBER: builtins.int GENERATED_OUTPUT_PKSCRIPT_FIELD_NUMBER: builtins.int + SILENT_PAYMENT_DLEQ_PROOF_FIELD_NUMBER: builtins.int type: global___BTCSignNextResponse.Type.ValueType index: builtins.int """index of the current input or output""" @@ -370,6 +371,7 @@ class BTCSignNextResponse(google.protobuf.message.Message): @property def anti_klepto_signer_commitment(self) -> antiklepto_pb2.AntiKleptoSignerCommitment: ... generated_output_pkscript: builtins.bytes + silent_payment_dleq_proof: builtins.bytes def __init__(self, *, type: global___BTCSignNextResponse.Type.ValueType = ..., @@ -379,9 +381,10 @@ class BTCSignNextResponse(google.protobuf.message.Message): prev_index: builtins.int = ..., anti_klepto_signer_commitment: typing.Optional[antiklepto_pb2.AntiKleptoSignerCommitment] = ..., generated_output_pkscript: builtins.bytes = ..., + silent_payment_dleq_proof: builtins.bytes = ..., ) -> None: ... def HasField(self, field_name: typing_extensions.Literal["anti_klepto_signer_commitment",b"anti_klepto_signer_commitment"]) -> builtins.bool: ... - 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","type",b"type"]) -> None: ... + 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: ... global___BTCSignNextResponse = BTCSignNextResponse class BTCSignInputRequest(google.protobuf.message.Message): diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6e0ac65c01..9a00685ffe 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -419,6 +419,8 @@ add_custom_target(rust-bindgen --allowlist-function wally_hash160 --allowlist-function wally_sha512 --allowlist-function printf + --allowlist-function bitbox_secp256k1_dleq_prove + --allowlist-function bitbox_secp256k1_dleq_verify ${CMAKE_CURRENT_SOURCE_DIR}/rust/bitbox02-sys/wrapper.h -- -DPB_NO_PACKED_STRUCTS=1 -DPB_FIELD_16BIT=1 -fshort-enums ${RUST_BINDGEN_FLAGS} ${RUST_INCLUDES} COMMAND diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index 06099e89e8..2ef4d2fa44 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -68,6 +68,7 @@ name = "bitbox02" version = "0.1.0" dependencies = [ "bitbox02-sys", + "bitcoin", "hex", "lazy_static", "util", @@ -702,6 +703,7 @@ name = "streaming-silent-payments" version = "0.1.0" dependencies = [ "bech32", + "bitbox02", "bitcoin", "hex", "serde", diff --git a/src/rust/bitbox02-rust/src/hww/api/bitcoin/signtx.rs b/src/rust/bitbox02-rust/src/hww/api/bitcoin/signtx.rs index 5fa17185c7..fab0172412 100644 --- a/src/rust/bitbox02-rust/src/hww/api/bitcoin/signtx.rs +++ b/src/rust/bitbox02-rust/src/hww/api/bitcoin/signtx.rs @@ -815,18 +815,17 @@ async fn _process(request: &pb::BtcSignInitRequest) -> Result { match silent_payment { None => return Err(Error::InvalidInput), Some(ref mut silent_payment) => { - if output_type != pb::BtcOutputType::P2tr { - return Err(Error::InvalidInput); - } - let xonly = silent_payment + let sp_output = silent_payment .create_output(&output_silent_payment.address) .map_err(|_| Error::InvalidInput)?; let payload = common::Payload { - data: xonly.serialize().to_vec(), - output_type, + data: sp_output.pubkey.serialize().to_vec(), + output_type: pb::BtcOutputType::P2tr, }; next_response.next.generated_output_pkscript = payload.pk_script(coin_params)?; + next_response.next.silent_payment_dleq_proof = + sp_output.dleq_proof.to_vec(); payload } } @@ -2434,9 +2433,10 @@ mod tests { transaction.borrow_mut().inputs[0].input.script_config_index = 1; transaction.borrow_mut().inputs[0].input.keypath[0] = 86 + HARDENED; - // Make first output a silent payment output. - transaction.borrow_mut().outputs[0].r#type = pb::BtcOutputType::P2tr as _; - transaction.borrow_mut().outputs[0].payload = b"\x7b\x91\x01\xd6\x0c\x64\x61\xff\x3e\x18\xf0\x83\x2e\x7f\x1e\x95\x20\x84\x20\x50\x62\xd7\xe0\xb7\xb0\x88\x12\xc2\x64\xcf\xe7\x13".to_vec(); + // Make first output a silent payment output. type and payload + // are ignored. + transaction.borrow_mut().outputs[0].r#type = pb::BtcOutputType::Unknown as _; + transaction.borrow_mut().outputs[0].payload = vec![]; transaction.borrow_mut().outputs[0].silent_payment = Some(pb::btc_sign_output_request::SilentPayment { address: "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv".into(), diff --git a/src/rust/bitbox02-rust/src/shiftcrypto.bitbox02.rs b/src/rust/bitbox02-rust/src/shiftcrypto.bitbox02.rs index d47fe9738f..a51c81be0a 100644 --- a/src/rust/bitbox02-rust/src/shiftcrypto.bitbox02.rs +++ b/src/rust/bitbox02-rust/src/shiftcrypto.bitbox02.rs @@ -520,6 +520,8 @@ pub struct BtcSignNextResponse { >, #[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 { @@ -622,8 +624,8 @@ pub struct BtcSignOutputRequest { pub script_config_index: u32, #[prost(uint32, optional, tag = "7")] pub payment_request_index: ::core::option::Option, - /// If provided, `type` must be `P2TR` and the payload must be empty. The output's pkScript is - /// returned BTCSignNextResponse. + /// If provided, `type` and `payload` is ignored. The generated output pkScript is returned in + /// BTCSignNextResponse. #[prost(message, optional, tag = "8")] pub silent_payment: ::core::option::Option, } diff --git a/src/rust/bitbox02-sys/wrapper.h b/src/rust/bitbox02-sys/wrapper.h index b3438f6344..84c01025a5 100644 --- a/src/rust/bitbox02-sys/wrapper.h +++ b/src/rust/bitbox02-sys/wrapper.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rust/bitbox02/Cargo.toml b/src/rust/bitbox02/Cargo.toml index c4b5eef056..ff92da470a 100644 --- a/src/rust/bitbox02/Cargo.toml +++ b/src/rust/bitbox02/Cargo.toml @@ -26,6 +26,7 @@ bitbox02-sys = {path="../bitbox02-sys"} util = {path = "../util"} zeroize = { workspace = true } lazy_static = { workspace = true, optional = true } +bitcoin = { workspace = true } [dev-dependencies] hex = { workspace = true } diff --git a/src/rust/bitbox02/src/secp256k1.rs b/src/rust/bitbox02/src/secp256k1.rs index 2ffccba9a5..f17a711e7f 100644 --- a/src/rust/bitbox02/src/secp256k1.rs +++ b/src/rust/bitbox02/src/secp256k1.rs @@ -1,4 +1,4 @@ -// Copyright 2022 Shift Crypto AG +// Copyright 2022-2024 Shift Crypto AG // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +use bitcoin::secp256k1::ffi::CPtr; + use alloc::vec::Vec; pub fn ecdsa_anti_exfil_host_commit(rand32: &[u8]) -> Result, ()> { @@ -27,3 +29,90 @@ pub fn ecdsa_anti_exfil_host_commit(rand32: &[u8]) -> Result, ()> { _ => Err(()), } } + +pub fn dleq_prove( + sk: &[u8; 32], + gen2: &bitcoin::secp256k1::PublicKey, + p1: &bitcoin::secp256k1::PublicKey, + p2: &bitcoin::secp256k1::PublicKey, +) -> Result, ()> { + let mut s = [0u8; 32]; + let mut e = [0u8; 32]; + let result = unsafe { + bitbox02_sys::bitbox_secp256k1_dleq_prove( + bitbox02_sys::wally_get_secp_context(), + s.as_mut_ptr(), + e.as_mut_ptr(), + sk.as_ptr(), + gen2.as_c_ptr() as _, + p1.as_c_ptr() as _, + p2.as_c_ptr() as _, + ) + }; + if result == 1 { + let mut result = s.to_vec(); + result.extend(&e); + Ok(result) + } else { + Err(()) + } +} + +pub fn dleq_verify( + proof: [u8; 64], + gen2: &bitcoin::secp256k1::PublicKey, + p1: &bitcoin::secp256k1::PublicKey, + p2: &bitcoin::secp256k1::PublicKey, +) -> Result<(), ()> { + let result = unsafe { + bitbox02_sys::bitbox_secp256k1_dleq_verify( + bitbox02_sys::wally_get_secp_context(), + proof[..32].as_ptr(), + proof[32..].as_ptr(), + p1.as_c_ptr() as _, + gen2.as_c_ptr() as _, + p2.as_c_ptr() as _, + ) + }; + if result == 1 { + Ok(()) + } else { + Err(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; + + #[test] + fn test_dleq() { + let secp = Secp256k1::new(); + let seckey_bytes = b"\x07\x7e\xb7\x5a\x52\xec\xa2\x4c\xde\xdf\x05\x8c\x92\xf1\xca\x8b\x9d\x48\x41\x77\x1f\xd6\xba\xa3\xd2\x78\x85\xfb\x5b\x49\xfb\xa2"; + let seckey = SecretKey::from_slice(seckey_bytes).unwrap(); + + let pubkey = seckey.public_key(&secp); + + let other_base_bytes = b"\x03\x89\x14\x0f\x7b\xb8\x52\xf0\x20\xf1\x54\xe5\x59\x08\xfe\x36\x99\xdc\x9f\x65\x15\x3e\x68\x15\x27\xf0\xd5\x5a\xab\xed\x93\x7f\x4b"; + let other_base = PublicKey::from_slice(other_base_bytes).unwrap(); + + let other_pubkey = other_base; + let other_pubkey = other_pubkey.mul_tweak(&secp, &seckey.into()).unwrap(); + let proof = dleq_prove(seckey_bytes, &other_base, &pubkey, &other_pubkey).unwrap(); + // Check against fixture so potential upstream changes in the DLEQ implementation get + // caught. Incompatible changes can break BitBox client libraries that rely on this + // specific DLEQ implementation. + assert_eq!( + hex::encode(&proof), + "6c885f825f6ce7565bc6d0bfda90506b11e2682dfe943f5a85badf1c8a96edc5f5e03f5ee2c58bf979646fbada920f9f1c5bd92805fb5b01534b42d26a550f79", + ); + dleq_verify( + proof.try_into().unwrap(), + &other_base, + &pubkey, + &other_pubkey, + ) + .unwrap(); + } +} diff --git a/src/rust/streaming-silent-payments/Cargo.toml b/src/rust/streaming-silent-payments/Cargo.toml index 455ab94da7..1934eea6b4 100644 --- a/src/rust/streaming-silent-payments/Cargo.toml +++ b/src/rust/streaming-silent-payments/Cargo.toml @@ -21,7 +21,8 @@ license = "Apache-2.0" [dependencies] bitcoin = { workspace = true } -bech32 = { version = "0.11.0", default-features = false } +bech32 = { workspace = true } +bitbox02 = {path = "../bitbox02" } [dev-dependencies] serde = { version = "1.0", features = ["derive"] } diff --git a/src/rust/streaming-silent-payments/src/lib.rs b/src/rust/streaming-silent-payments/src/lib.rs index 747b6a2e26..d8faa10877 100644 --- a/src/rust/streaming-silent-payments/src/lib.rs +++ b/src/rust/streaming-silent-payments/src/lib.rs @@ -20,10 +20,15 @@ mod hash; pub use bitcoin; use bitcoin::hashes::Hash; -use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey, XOnlyPublicKey}; +use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey, XOnlyPublicKey}; use alloc::vec::Vec; +pub struct Output { + pub pubkey: XOnlyPublicKey, + pub dleq_proof: [u8; 33 + 64], +} + pub enum Network { Btc, Tbtc, @@ -46,7 +51,7 @@ pub struct SilentPayment { // Done streaming inputs? inputs_done: bool, // We only allow one silent payment output for now. This tracks whether we've seen it. - output_checked: bool, + output_created: bool, } fn calculate_t_k(ecdh_shared_secret: &PublicKey, k: u32) -> Result { @@ -99,7 +104,7 @@ impl SilentPayment { smallest_outpoint: None, a_sum: None, inputs_done: false, - output_checked: false, + output_created: false, } } @@ -137,7 +142,7 @@ impl SilentPayment { match self.a_sum { None => self.a_sum = Some(negated_key), Some(ref mut p) => { - *p = p.add_tweak(&negated_key.into()).map_err(|_| ())?; + *p = p.add_tweak(&Scalar::from(negated_key)).map_err(|_| ())?; } } @@ -147,12 +152,12 @@ impl SilentPayment { /// Call this for silent payment outputs. /// `silent_payment_address` is the output address. /// This returns the SegWit v1 Taproot output key of the created output. - pub fn create_output(&mut self, silent_payment_address: &str) -> Result { + pub fn create_output(&mut self, silent_payment_address: &str) -> Result { self.inputs_done = true; - if self.output_checked { + if self.output_created { return Err(()); } - self.output_checked = true; + self.output_created = true; let (scan_pubkey, m_pubkey) = decode_address(silent_payment_address, self.network.sp_hrp())?; @@ -170,7 +175,7 @@ impl SilentPayment { .mul_tweak(&self.secp, &partial_secret.into()) .map_err(|_| ())?; - // If we want to support more than one silent pay4ment output, we need to get this value from + // If we want to support more than one silent payment output, we need to get this value from // the host per output, and check before signing the tx that for each SP output with the // same scan pubkey has a different `k` and they are consecutive starting at 0, so the // recipient is sure to be able to find the output. With only one silent payment output @@ -182,7 +187,30 @@ impl SilentPayment { let res = t_k.public_key(&self.secp); let reskey = res.combine(&m_pubkey).map_err(|_| ())?; let (reskey_xonly, _) = reskey.x_only_public_key(); - Ok(reskey_xonly) + + let dleq_proof: [u8; 33 + 64] = { + #[allow(non_snake_case)] + let C = scan_pubkey + .mul_tweak(&self.secp, &Scalar::from(*a_sum)) + .map_err(|_| ())?; + + let mut v = C.serialize().to_vec(); + let proof = bitbox02::secp256k1::dleq_prove(a_sum.as_ref(), &scan_pubkey, &A_sum, &C)?; + // Sanity check. + bitbox02::secp256k1::dleq_verify( + proof.as_slice().try_into().unwrap(), + &scan_pubkey, + &A_sum, + &C, + )?; + v.extend(&proof); + v.try_into().unwrap() + }; + + Ok(Output { + pubkey: reskey_xonly, + dleq_proof, + }) } } @@ -232,11 +260,14 @@ mod tests { "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16:0" ); - let expected = XOnlyPublicKey::from_str( + let expected_pubkey = XOnlyPublicKey::from_str( "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1", ) .unwrap(); - assert_eq!(v.create_output("sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv").unwrap(), expected); + let expected_dleq_proof = "02bd6cf6542e272a81a6aba9d35c0140d73758ad74c992d8808c0f0d76a642fe9977ecc511315c7fa44e54af3676ee212ca21031ef4a763dc841a49b59431ef8e4e2ac48d74324d5115602e2720c365c836da738f8c43c513f0022a40d6e71a048"; + let output = v.create_output("sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv").unwrap(); + assert_eq!(output.pubkey, expected_pubkey); + assert_eq!(hex::encode(output.dleq_proof), expected_dleq_proof); } #[test] diff --git a/src/rust/streaming-silent-payments/tests/table_test.rs b/src/rust/streaming-silent-payments/tests/table_test.rs index 38922af5df..b3c76aeace 100644 --- a/src/rust/streaming-silent-payments/tests/table_test.rs +++ b/src/rust/streaming-silent-payments/tests/table_test.rs @@ -170,7 +170,7 @@ fn test_sending() { } else if pk_script.is_p2sh() { panic!("tests don't include p2sh - parse for p2sh-p2wpkh if needed") } else { - panic!("unreconigzed input") + panic!("unrecognized input") }; v.add_input( input_type, @@ -180,7 +180,7 @@ fn test_sending() { .unwrap(); } - assert_eq!(v.create_output(sp_address).unwrap(), expected); + assert_eq!(v.create_output(sp_address).unwrap().pubkey, expected); } } }