sign_tx: return the error message when additional_info parsing fails
What changed, and why it matters
This commit fixes a minor user-experience and diagnostic issue in Blockstream Jade's transaction signing code. Previously, when the optional 'additional_info' block for Liquid transactions could not be parsed or validated, the device rejected the request without telling the caller exactly what went wrong. After the patch, the specific error message is passed back to the caller. There is no direct evidence this change fixes an exploitable security vulnerability.
Treat as a routine quality/diagnostic improvement. No urgent security action is indicated by the diff alone. If this commit is part of a larger release, review the release notes for any additional security context.
Security signals we found
Error-message-only change: no logic that validates assets, amounts, signatures, or transaction structure is modified
No new bounds checks, memory allocations, or cryptographic operations introduced
No mention of vulnerability, CVE, security bug, or researcher attribution in commit title or message
Diff shows removal of internal reject calls in favor of returning errmsg to caller
Evidence from the diff
The change refactors params_additional_info() and validate_additional_info() to return an error string pointer (const char** errmsg) rather than immediately emitting a jade_process_reject_message() internally. Callers in sign_tx.c and sign_psbt.c now receive the message and can forward it in their own rejection responses. It also adds a new explicit error message when tx-type extraction fails. The patch is purely about error propagation and messaging; no cryptographic, parsing, or authorization logic is altered.
Changed components
main/process/sign_tx.cmain/process/sign_psbt.cmain/process/sign_utils.cmain/process/sign_utils.hInspect captured patch +23 / −25
diff --git a/main/process/sign_psbt.c b/main/process/sign_psbt.c
index 3fb6d94..140a709 100644
--- a/main/process/sign_psbt.c
+++ b/main/process/sign_psbt.c
@@ -693,8 +693,8 @@ int sign_psbt(jade_process_t* process, CborValue* params, const network_t networ
// Liquid: Get any data from the optional 'additional_info' section
if (for_liquid && process && params
&& !params_additional_info(
- process, params, tx, &txtype, &is_partial, &in_sums, &num_in_sums, &out_sums, &num_out_sums)) {
- // Note in_sums/out_sums are cleared automatically at proces exit
+ process, params, tx, &txtype, &is_partial, &in_sums, &num_in_sums, &out_sums, &num_out_sums, errmsg)) {
+ // Note in_sums/out_sums are cleared automatically at process exit
retval = CBOR_RPC_BAD_PARAMETERS;
goto cleanup_tx;
}
diff --git a/main/process/sign_tx.c b/main/process/sign_tx.c
index 487b9a5..87fe2da 100644
--- a/main/process/sign_tx.c
+++ b/main/process/sign_tx.c
@@ -464,6 +464,7 @@ static void sign_tx_impl(jade_process_t* process, const bool for_liquid)
JADE_LOGI("Read %d assets from message", num_assets);
}
+ const char* errmsg = NULL;
asset_summary_t *in_sums = NULL, *out_sums = NULL;
size_t num_in_sums = 0, num_out_sums = 0;
bool is_partial = false;
@@ -471,14 +472,14 @@ static void sign_tx_impl(jade_process_t* process, const bool for_liquid)
// Liquid: Get any data from the optional 'additional_info' section
if (for_liquid
&& !params_additional_info(
- process, ¶ms, tx, &txtype, &is_partial, &in_sums, &num_in_sums, &out_sums, &num_out_sums)) {
+ process, ¶ms, tx, &txtype, &is_partial, &in_sums, &num_in_sums, &out_sums, &num_out_sums, &errmsg)) {
+ jade_process_reject_message(process, CBOR_RPC_BAD_PARAMETERS, errmsg);
goto cleanup;
}
// Liquid: Gather the (unblinded) output info for user confirmation,
// then validate output and additional_info values
if (for_liquid) {
- const char* errmsg = NULL;
if (!update_elements_outputs(tx, commitments, output_info, &errmsg)
|| !validate_elements_outputs(
network_id, tx, txtype, output_info, in_sums, num_in_sums, out_sums, num_out_sums, &errmsg)) {
@@ -569,7 +570,6 @@ static void sign_tx_impl(jade_process_t* process, const bool for_liquid)
// (But if passed must be valid - empty/root path is not allowed for signing)
const bool has_path = rpc_has_field_data("path", ¶ms);
if (has_path) {
- const char* errmsg = NULL;
num_to_sign += 1;
// Get all common tx-signing input fields which must be present if a path is given
diff --git a/main/process/sign_utils.c b/main/process/sign_utils.c
index 5f0074c..7de2dd0 100644
--- a/main/process/sign_utils.c
+++ b/main/process/sign_utils.c
@@ -155,21 +155,21 @@ static void rpc_get_asset_summary(
}
}
-static bool validate_additional_info(jade_process_t* process, const struct wally_tx* tx, const TxType_t txtype,
- const bool is_partial, const asset_summary_t* in_sums, const size_t num_in_sums, const asset_summary_t* out_sums,
- const size_t num_out_sums)
+static bool validate_additional_info(const struct wally_tx* tx, const TxType_t txtype, const bool is_partial,
+ const asset_summary_t* in_sums, const size_t num_in_sums, const asset_summary_t* out_sums,
+ const size_t num_out_sums, const char** errmsg)
{
- const char* errmsg = NULL;
// Shouldn't have pointers to empty arrays
JADE_ASSERT(!in_sums == !num_in_sums);
JADE_ASSERT(!out_sums == !num_out_sums);
+ JADE_INIT_OUT_PPTR(errmsg);
// Validate tx type data
if (txtype == TXTYPE_SWAP) {
// Input and output summary must be present - they will be fully validated later
if (!in_sums || !out_sums) {
- errmsg = "Swap tx missing input/output summary information";
- goto done;
+ *errmsg = "Swap tx missing input/output summary information";
+ return false;
}
// Validate swap or proposal appears to have expected inputs and outputs
@@ -178,30 +178,26 @@ static bool validate_additional_info(jade_process_t* process, const struct wally
// input and exactly one output which is to self, and in a different asset to the input
if (tx->num_inputs != 1 || tx->num_outputs != 1 || num_in_sums != 1 || num_out_sums != 1
|| !memcmp(in_sums[0].asset_id, out_sums[0].asset_id, sizeof(out_sums[0].asset_id))) {
- errmsg = "Initial swap proposal must have single wallet input and output in different assets";
- goto done;
+ *errmsg = "Initial swap proposal must have single wallet input and output in different assets";
+ return false;
}
} else {
// TODO: Ideally check total number of assets in our inputs and outputs
if (tx->num_inputs < 2 || tx->num_outputs < 2) {
- errmsg = "Insufficient inputs/outputs for a swap tx";
- goto done;
+ *errmsg = "Insufficient inputs/outputs for a swap tx";
+ return false;
}
}
} else if (txtype != TXTYPE_SEND_PAYMENT) {
- errmsg = "Unsupported tx-type in additional info";
- goto done;
- }
-done:
- if (errmsg) {
- jade_process_reject_message(process, CBOR_RPC_BAD_PARAMETERS, errmsg);
+ *errmsg = "Unsupported tx-type in additional info";
return false;
}
return true;
}
TxType_t params_additional_info(jade_process_t* process, CborValue* params, const struct wally_tx* tx, TxType_t* txtype,
- bool* is_partial, asset_summary_t** in_sums, size_t* num_in_sums, asset_summary_t** out_sums, size_t* num_out_sums)
+ bool* is_partial, asset_summary_t** in_sums, size_t* num_in_sums, asset_summary_t** out_sums, size_t* num_out_sums,
+ const char** errmsg)
{
JADE_ASSERT(params);
JADE_ASSERT(tx);
@@ -211,6 +207,7 @@ TxType_t params_additional_info(jade_process_t* process, CborValue* params, cons
JADE_INIT_OUT_SIZE(num_in_sums);
JADE_INIT_OUT_PPTR(out_sums);
JADE_INIT_OUT_SIZE(num_out_sums);
+ JADE_INIT_OUT_PPTR(errmsg);
*is_partial = false;
*txtype = TXTYPE_SEND_PAYMENT;
@@ -230,10 +227,10 @@ TxType_t params_additional_info(jade_process_t* process, CborValue* params, cons
// Tx Type
if (!rpc_get_txtype(process, &additional_info, txtype)) {
+ *errmsg = "Failed to extract tx type from additional_info";
return false;
}
- if (!validate_additional_info(
- process, tx, *txtype, *is_partial, *in_sums, *num_in_sums, *out_sums, *num_out_sums)) {
+ if (!validate_additional_info(tx, *txtype, *is_partial, *in_sums, *num_in_sums, *out_sums, *num_out_sums, errmsg)) {
return false;
}
return true;
diff --git a/main/process/sign_utils.h b/main/process/sign_utils.h
index 83a9eb7..aa021a3 100644
--- a/main/process/sign_utils.h
+++ b/main/process/sign_utils.h
@@ -26,7 +26,8 @@ bool params_trusted_commitments(
jade_process_t* process, const CborValue* params, const struct wally_tx* tx, commitment_t** data);
TxType_t params_additional_info(jade_process_t* process, CborValue* params, const struct wally_tx* tx, TxType_t* txtype,
- bool* is_partial, asset_summary_t** in_sums, size_t* num_in_sums, asset_summary_t** out_sums, size_t* num_out_sums);
+ bool* is_partial, asset_summary_t** in_sums, size_t* num_in_sums, asset_summary_t** out_sums, size_t* num_out_sums,
+ const char** errmsg);
// Returns true if commitments are present and validated correctly.
// Returns false otherwise, with errmsg set if an error occurred, or
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.