Skip to content

Commit

Permalink
psbt: recognise and auto-verify Green-multisig change outputs
Browse files Browse the repository at this point in the history
  • Loading branch information
JamieDriver committed Jul 16, 2024
1 parent 27da625 commit ba71841
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 7 deletions.
103 changes: 96 additions & 7 deletions main/process/sign_psbt.c
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,76 @@ static bool is_green_multisig_signers(
return true;
}

// Generate a green-multisig script and test whether it matches the passed target_script
static bool verify_ga_script_matches_impl(const char* network, const uint32_t* path, const size_t path_len,
const struct pubkey_data* recovery_key, const size_t csv_blocks, const uint8_t* target_script,
const size_t target_script_len)
{
JADE_ASSERT(network);
JADE_ASSERT(path);
JADE_ASSERT(recovery_key);
JADE_ASSERT(target_script);
JADE_ASSERT(target_script_len);

size_t trial_script_len = 0;
uint8_t trial_script[WALLY_SCRIPTPUBKEY_P2WSH_LEN]; // Sufficient

if (!wallet_build_ga_script_ex(network, recovery_key->key, recovery_key->key_len, csv_blocks, path, path_len,
trial_script, sizeof(trial_script), &trial_script_len)) {
// Failed to build script
JADE_LOGE("Receive script cannot be constructed");
return false;
}

// Compare generated script to that expected/in the txn
if (trial_script_len != target_script_len || sodium_memcmp(target_script, trial_script, trial_script_len) != 0) {
JADE_LOGW("Receive script failed validation");
return false;
}

// Script matches
return true;
}

// Generate green-multisig scripts for multisig and for any possible csv scripts and test whether any match
static bool verify_ga_script_matches(const char* network, const uint32_t* path, const size_t path_len,
const struct pubkey_data* recovery_key, const uint8_t* target_script, const size_t target_script_len)
{
JADE_ASSERT(network);
JADE_ASSERT(path);
JADE_ASSERT(recovery_key);
JADE_ASSERT(target_script);
JADE_ASSERT(target_script_len);

// NOTE: 2of3 csv not supported
if (!recovery_key || !recovery_key->key_len) {
// Try each of the allowed csv blocks
const size_t* allowed_csv_blocks = NULL;
const size_t num_allowed = csvBlocksForNetwork(network, &allowed_csv_blocks);
JADE_ASSERT(num_allowed);
JADE_ASSERT(allowed_csv_blocks);

for (size_t i = 0; i < num_allowed; ++i) {
if (verify_ga_script_matches_impl(
network, path, path_len, recovery_key, allowed_csv_blocks[i], target_script, target_script_len)) {
// csv script match
return true;
}
}
}

// Check 2of2/2of3 legacy multisig
const size_t csv_blocks = 0;
if (verify_ga_script_matches_impl(
network, path, path_len, recovery_key, csv_blocks, target_script, target_script_len)) {
// Legacy multisig w/o csv
return true;
}

// No csv values match
return false;
}

// Helper to generate a singlesig script of the given type with the pubkey given, and
// compare it to the target script provided.
// Returns true if then generated script matches the target script.
Expand Down Expand Up @@ -294,7 +364,7 @@ static bool verify_multisig_script_matches(const multisig_data_t* multisig_data,

// Build multisig script
size_t trial_script_len = 0;
uint8_t trial_script[WALLY_SCRIPTPUBKEY_P2WSH_LEN]; // Sufficient];
uint8_t trial_script[WALLY_SCRIPTPUBKEY_P2WSH_LEN]; // Sufficient;
if (!wallet_build_multisig_script(multisig_data->variant, multisig_data->sorted, multisig_data->threshold, pubkeys,
pubkeys_len, trial_script, sizeof(trial_script), &trial_script_len)) {
JADE_LOGE("Receive script cannot be constructed");
Expand Down Expand Up @@ -326,7 +396,7 @@ static bool verify_descriptor_script_matches_impl(const char* descriptor_name, c
// Build descriptor script
const char* errmsg = NULL;
size_t trial_script_len = 0;
uint8_t trial_script[WALLY_SCRIPTPUBKEY_P2WSH_LEN]; // Sufficient];
uint8_t trial_script[WALLY_SCRIPTPUBKEY_P2WSH_LEN]; // Sufficient;
if (!wallet_build_descriptor_script(network, descriptor_name, descriptor, multi_index, index, trial_script,
sizeof(trial_script), &trial_script_len, &errmsg)) {
JADE_LOGE("Receive script cannot be constructed");
Expand Down Expand Up @@ -571,15 +641,30 @@ static void validate_any_change_outputs(const char* network, struct wally_psbt*
"Unusual receive path: %s", have_path_str ? path_str : "too long");
JADE_ASSERT(ret > 0 && ret < sizeof(output_info[index].message));
}
} else if (num_keys > 1) {
} else if (signing_flags == (PSBT_SIGNING_GREEN_MULTISIG | PSBT_SIGNING_MULTISIG_CHANGE_ABANDONED)) {
// Signed only Green multisig inputs, only consider similar outputs
JADE_ASSERT(our_key_index < num_keys);

// Skip if we did not sign *only* multisig inputs for a single multisig record
if (signing_flags != (PSBT_SIGNING_MULTISIG | PSBT_SIGNING_SINGLE_MULTISIG_RECORD)) {
JADE_LOGD(
"Ignoring multisig output %u as not signing only multisig inputs for a single registration", index);
struct pubkey_data recovery_pubkey = { .key_len = 0 };
if (!is_green_multisig_signers(network, &output->keypaths, &recovery_pubkey)) {
JADE_LOGD("Ignoring non-green-multisig output %u as only signing green-multisig inputs", index);
continue;
}

if (!verify_ga_script_matches(network, path, path_len, &recovery_pubkey, tx_script, tx_script_len)) {
JADE_LOGD("Receive script failed validation for Green multisig");
continue;
}

// Change path valid and matches expected output script
JADE_LOGI("Output %u green-multisig path/script validated", index);

// Set appropriate flags - note Green wallet-output is always assumed to be change
output_info[index].flags |= (OUTPUT_FLAG_VALIDATED | OUTPUT_FLAG_CHANGE);

} else if (signing_flags == (PSBT_SIGNING_MULTISIG | PSBT_SIGNING_SINGLE_MULTISIG_RECORD)) {
// Generic multisig or descriptor
JADE_ASSERT(our_key_index < num_keys);
JADE_ASSERT(!multisig_data != !descriptor); // one or the other

// Get the path tail after the last hardened element
Expand Down Expand Up @@ -619,6 +704,10 @@ static void validate_any_change_outputs(const char* network, struct wally_psbt*
"Unusual change path suffix: %s", have_path_str ? path_str : "too long");
JADE_ASSERT(ret > 0 && ret < sizeof(output_info[index].message));
}
} else {
// Skip if we did not sign *only* multisig inputs for a single multisig record
JADE_LOGI(
"Ignoring multisig output %u as not signing only multisig inputs for a single registration", index);
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions test_data/psbt_tm_green_multisig_2of2csv.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"input": {
"network": "mainnet",
"psbt": "cHNidP8BAgQCAAAAAQMESQENAAEEAQEBBQECAQYBAwH7BAIAAAAAAQCWAgAAAAHGfFKkpmkwz6lla8vsaM6NSKcPhzIgLTkpoOfwwE/7SAEAAAAjIgAgjstQajfDLgpC3rf56fwhLpQezsSXlPkShFqX7po3LNf9////AigjAAAAAAAAF6kUwl4l9ocRtxv5C/H1k8GG8PPQ7jiHamIAAAAAAAAXqRTaaDUQQKTe6TsKYw5Xnfp88K0CbodbAQ0AAQEgKCMAAAAAAAAXqRTCXiX2hxG3G/kL8fWTwYbw89DuOIcBBCIAIBLudTRZunnYCKxGf8T0/8tn6G9/6NagGhHHB0fCFnFVAQVOIQO7lEkrpDqOX39lZbtRJqHQ6ugSTNEkElR3oj+3T2/Ema0hAhzl58ErP3u4+8RfWLTGS1XCPObLJVmC7ZM6cJl+bC/MrHNkA4DKALJoIgYCHOXnwSs/e7j7xF9YtMZLVcI85sslWYLtkzpwmX5sL8yMlqWp3wEAAADJAAAAj2cAAJ29AABqnwAAvZYAACJDAABWHQAAO3MAALpaAABSjwAAh4QAACtgAABOiQAA0HIAAG71AAAPOAAAFH0AAGVbAACdYwAA7rcAAFJPAAA/igAAuM8AAHsnAAC7DAAAAOoAAGTvAAB6dgAAHlMAAESaAACsfAAAvL8AAE8AAAAiBgO7lEkrpDqOX39lZbtRJqHQ6ugSTNEkElR3oj+3T2/EmQzj68x5AQAAAE8AAAABDiBKOC8lg5yOi/ju50u685e7oub88LMz1LhsMSQGib8IFAEPBAAAAAABEAT9////AAEDCJQRAAAAAAAAAQQXqRS4As4VN2kESI67GvrOMaXUE5LbdIcAIgICTffX8vQ2ai6qYJTbqIhdgNYRUfbACvO5WrlxtcvrxeGMlqWp3wEAAADJAAAAj2cAAJ29AABqnwAAvZYAACJDAABWHQAAO3MAALpaAABSjwAAh4QAACtgAABOiQAA0HIAAG71AAAPOAAAFH0AAGVbAACdYwAA7rcAAFJPAAA/igAAuM8AAHsnAAC7DAAAAOoAAGTvAAB6dgAAHlMAAESaAACsfAAAvL8AAFIAAAAiAgPkRhE9A3Fwswu9XWJKFLSIIuVislT3jM5hJG1Z6N/Yzwzj68x5AQAAAFIAAAABAwiMDQAAAAAAAAEEF6kUO1lBdHqB5DO7BObiQYhja2RyBvCHAA=="
},
"expected_output": {
"psbt": "cHNidP8BAgQCAAAAAQMESQENAAEEAQEBBQECAQYBAwH7BAIAAAAAAQCWAgAAAAHGfFKkpmkwz6lla8vsaM6NSKcPhzIgLTkpoOfwwE/7SAEAAAAjIgAgjstQajfDLgpC3rf56fwhLpQezsSXlPkShFqX7po3LNf9////AigjAAAAAAAAF6kUwl4l9ocRtxv5C/H1k8GG8PPQ7jiHamIAAAAAAAAXqRTaaDUQQKTe6TsKYw5Xnfp88K0CbodbAQ0AAQEgKCMAAAAAAAAXqRTCXiX2hxG3G/kL8fWTwYbw89DuOIciAgO7lEkrpDqOX39lZbtRJqHQ6ugSTNEkElR3oj+3T2/EmUcwRAIgR4zJqk09JW32KcGeoyWbIbNUi61zbVhc0h+Cfci7coYCIGHEjRcZ2oR/WP2F76Uml1wktHJsiYaQw5HMcNJrR7ooAQEEIgAgEu51NFm6edgIrEZ/xPT/y2fob3/o1qAaEccHR8IWcVUBBU4hA7uUSSukOo5ff2Vlu1EmodDq6BJM0SQSVHeiP7dPb8SZrSECHOXnwSs/e7j7xF9YtMZLVcI85sslWYLtkzpwmX5sL8ysc2QDgMoAsmgiBgIc5efBKz97uPvEX1i0xktVwjzmyyVZgu2TOnCZfmwvzIyWpanfAQAAAMkAAACPZwAAnb0AAGqfAAC9lgAAIkMAAFYdAAA7cwAAuloAAFKPAACHhAAAK2AAAE6JAADQcgAAbvUAAA84AAAUfQAAZVsAAJ1jAADutwAAUk8AAD+KAAC4zwAAeycAALsMAAAA6gAAZO8AAHp2AAAeUwAARJoAAKx8AAC8vwAATwAAACIGA7uUSSukOo5ff2Vlu1EmodDq6BJM0SQSVHeiP7dPb8SZDOPrzHkBAAAATwAAAAEOIEo4LyWDnI6L+O7nS7rzl7ui5vzwszPUuGwxJAaJvwgUAQ8EAAAAAAEQBP3///8AAQMIlBEAAAAAAAABBBepFLgCzhU3aQRIjrsa+s4xpdQTktt0hwAiAgJN99fy9DZqLqpglNuoiF2A1hFR9sAK87lauXG1y+vF4YyWpanfAQAAAMkAAACPZwAAnb0AAGqfAAC9lgAAIkMAAFYdAAA7cwAAuloAAFKPAACHhAAAK2AAAE6JAADQcgAAbvUAAA84AAAUfQAAZVsAAJ1jAADutwAAUk8AAD+KAAC4zwAAeycAALsMAAAA6gAAZO8AAHp2AAAeUwAARJoAAKx8AAC8vwAAUgAAACICA+RGET0DcXCzC71dYkoUtIgi5WKyVPeMzmEkbVno39jPDOPrzHkBAAAAUgAAAAEDCIwNAAAAAAAAAQQXqRQ7WUF0eoHkM7sE5uJBiGNrZHIG8IcA"
}
}
9 changes: 9 additions & 0 deletions test_data/psbt_tm_green_multisig_2of3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"input": {
"network": "mainnet",
"psbt": "cHNidP8BAgQCAAAAAQMEWwENAAEEAQEBBQECAQYBAwH7BAIAAAAAAQCWAgAAAAFKOC8lg5yOi/ju50u685e7oub88LMz1LhsMSQGib8IFAEAAAAjIgAg1uOIVkVdMAEtH3Q1v81uQIyX+atE4JNRf1tdkLUdec/9////AigjAAAAAAAAF6kU3U9YPry8S4KKfLHNwHhNuTPW596HvTkAAAAAAAAXqRRieGTzQeBRZ1UIoiy6BBWy4uepVodbAQ0AAQEgKCMAAAAAAAAXqRTdT1g+vLxLgop8sc3AeE25M9bn3ocBBCIAIM+By6fTdfyYvXi/yntiKqMimXmB2ogqS/l3ESobmLoZAQVpUiECnP3Zoe7ftiov/hEJgNcV8IT13Og8VVEtCwz851x+jHohA1h++i25jFKKeY5QAOOO2XmTVXcN5KUlh65z3tQWdxg0IQKJ/ZZbf/caG5W9XbKV1nBfX4dbEHXzbVfOuqpLUicLmFOuIgYCnP3Zoe7ftiov/hEJgNcV8IT13Og8VVEtCwz851x+jHqQlqWp3wMAAADJAAAAj2cAAJ29AABqnwAAvZYAACJDAABWHQAAO3MAALpaAABSjwAAh4QAACtgAABOiQAA0HIAAG71AAAPOAAAFH0AAGVbAACdYwAA7rcAAFJPAAA/igAAuM8AAHsnAAC7DAAAAOoAAGTvAAB6dgAAHlMAAESaAACsfAAAvL8AAAMAAAABAAAAIgYDWH76LbmMUop5jlAA447ZeZNVdw3kpSWHrnPe1BZ3GDQU4+vMeQMAAIADAACAAQAAAAEAAAAiBgKJ/ZZbf/caG5W9XbKV1nBfX4dbEHXzbVfOuqpLUicLmAyLl34LAQAAAAEAAAABDiBAB/f1WbhKVfWFWBzlhfJmaW2mKIUYSSkKjBwFET4gpwEPBAAAAAABEAT9////AAEDCJQRAAAAAAAAAQQXqRS4As4VN2kESI67GvrOMaXUE5LbdIcAIgIDSieTMo/qVaQ6ge55BYfcGmlMnuAd3au8r0uEHfLlxqyQlqWp3wMAAADJAAAAj2cAAJ29AABqnwAAvZYAACJDAABWHQAAO3MAALpaAABSjwAAh4QAACtgAABOiQAA0HIAAG71AAAPOAAAFH0AAGVbAACdYwAA7rcAAFJPAAA/igAAuM8AAHsnAAC7DAAAAOoAAGTvAAB6dgAAHlMAAESaAACsfAAAvL8AAAMAAAACAAAAIgICbQViVClQStIXCNtcKtkOyS1cZ1yUz5usECm8Bm3emucU4+vMeQMAAIADAACAAQAAAAIAAAAiAgNGpNhv7DCpF+TodyrnG7/qLYbQxy+H/MFgn3Fy6ACTFQyLl34LAQAAAAIAAAABAwhpDQAAAAAAAAEEF6kUje2RGBbKEX93SQ/a+SIaYXGoHWaHAA=="
},
"expected_output": {
"psbt": "cHNidP8BAgQCAAAAAQMEWwENAAEEAQEBBQECAQYBAwH7BAIAAAAAAQCWAgAAAAFKOC8lg5yOi/ju50u685e7oub88LMz1LhsMSQGib8IFAEAAAAjIgAg1uOIVkVdMAEtH3Q1v81uQIyX+atE4JNRf1tdkLUdec/9////AigjAAAAAAAAF6kU3U9YPry8S4KKfLHNwHhNuTPW596HvTkAAAAAAAAXqRRieGTzQeBRZ1UIoiy6BBWy4uepVodbAQ0AAQEgKCMAAAAAAAAXqRTdT1g+vLxLgop8sc3AeE25M9bn3ociAgNYfvotuYxSinmOUADjjtl5k1V3DeSlJYeuc97UFncYNEcwRAIgEV8I+sOWAKcJ5z16ZD/rTgCKUzb0gLfLS2t4kGpPCoYCIDOOwEsMBGvJaUz/hV/ZxjXlSHFmNtuVffsTmOiM4HEPAQEEIgAgz4HLp9N1/Ji9eL/Ke2IqoyKZeYHaiCpL+XcRKhuYuhkBBWlSIQKc/dmh7t+2Ki/+EQmA1xXwhPXc6DxVUS0LDPznXH6MeiEDWH76LbmMUop5jlAA447ZeZNVdw3kpSWHrnPe1BZ3GDQhAon9llt/9xoblb1dspXWcF9fh1sQdfNtV866qktSJwuYU64iBgKc/dmh7t+2Ki/+EQmA1xXwhPXc6DxVUS0LDPznXH6MepCWpanfAwAAAMkAAACPZwAAnb0AAGqfAAC9lgAAIkMAAFYdAAA7cwAAuloAAFKPAACHhAAAK2AAAE6JAADQcgAAbvUAAA84AAAUfQAAZVsAAJ1jAADutwAAUk8AAD+KAAC4zwAAeycAALsMAAAA6gAAZO8AAHp2AAAeUwAARJoAAKx8AAC8vwAAAwAAAAEAAAAiBgNYfvotuYxSinmOUADjjtl5k1V3DeSlJYeuc97UFncYNBTj68x5AwAAgAMAAIABAAAAAQAAACIGAon9llt/9xoblb1dspXWcF9fh1sQdfNtV866qktSJwuYDIuXfgsBAAAAAQAAAAEOIEAH9/VZuEpV9YVYHOWF8mZpbaYohRhJKQqMHAURPiCnAQ8EAAAAAAEQBP3///8AAQMIlBEAAAAAAAABBBepFLgCzhU3aQRIjrsa+s4xpdQTktt0hwAiAgNKJ5Myj+pVpDqB7nkFh9waaUye4B3dq7yvS4Qd8uXGrJCWpanfAwAAAMkAAACPZwAAnb0AAGqfAAC9lgAAIkMAAFYdAAA7cwAAuloAAFKPAACHhAAAK2AAAE6JAADQcgAAbvUAAA84AAAUfQAAZVsAAJ1jAADutwAAUk8AAD+KAAC4zwAAeycAALsMAAAA6gAAZO8AAHp2AAAeUwAARJoAAKx8AAC8vwAAAwAAAAIAAAAiAgJtBWJUKVBK0hcI21wq2Q7JLVxnXJTPm6wQKbwGbd6a5xTj68x5AwAAgAMAAIABAAAAAgAAACICA0ak2G/sMKkX5Oh3Kucbv+othtDHL4f8wWCfcXLoAJMVDIuXfgsBAAAAAgAAAAEDCGkNAAAAAAAAAQQXqRSN7ZEYFsoRf3dJD9r5IhphcagdZocA"
}
}

0 comments on commit ba71841

Please sign in to comment.