Label tweaks returned by the label_lookup callback that overflow the group order were silently reduced. recipient_label_create never produces such tweaks, so an overflowing one means the caller's label cache is broken. As suggested in #1929 (comment), this PR treats it as API misuse using ARG_CHECK (illegal callback, return 0), documents the requirement. It also adds a test and a changelog entry.
silentpayments: reject label tweaks that overflow the group order #1974
pull theStack wants to merge 1 commits into bitcoin-core:master from theStack:check-label_cache-tweak-overflow changing 2 files +13 −2-
theStack commented at 3:05 AM on October 22, 2026: contributor
-
real-or-random commented at 9:20 PM on October 22, 2026: contributor
Concept ACK
I tend to thinkl the Changelog entry really adds more confusion then it solves. Any caller who hits this is clearly violates the API contract (in a very weird way).
- theStack force-pushed on Oct 23, 2026
-
theStack commented at 1:47 AM on October 23, 2026: contributor
I tend to thinkl the Changelog entry really adds more confusion then it solves. Any caller who hits this is clearly violates the API contract (in a very weird way).
Makes sense, dropped the changelog commit.
-
fametrano commented at 4:10 AM on October 23, 2026: contributor
tACK b5fb2adf090d49b034db86b60a740d6058743ff7
I built the head with CMake on macOS arm64 and ran ctest: all 231 tests pass. I also wrote a small program against the public API that scans a one-output labeled transaction with several tweaks in the label cache, and ran it against the head and the merge base. With the tweak from
recipient_label_create, both find the output and return a tweak that spends it. With the group order or 2^256-1, the merge base returns 1 and a tweak that does not spend the output. The head calls the illegal callback, returns 0, reports no found outputs and leaves the found-output entry untouched. I can share the program if useful. -
fametrano commented at 6:07 AM on October 23, 2026: contributor
Under valgrind's constant-time check, the new
if (tweak_overflow)branches on a secret value when the label tweak is marked secret.I added to
ctime_testsa scan that finds an output sent to a labeled address, with the scan key and the label tweak (the valuelabel_lookupreturns) marked secret, and ran it under valgrind. Valgrind reports "Conditional jump or move depends on uninitialised value(s)" atif (tweak_overflow) {insecp256k1_silentpayments_recipient_scan_outputs. The same test gives 0 errors on the base commit. With the one-linesecp256k1_declassifysuggested inline, the run is clean and the output is still found.I can post the test here, or open a follow-up PR that adds it to
src/ctime_tests.c. -
in src/modules/silentpayments/main_impl.h:759 in b5fb2adf09 outdated
755 | * break indistinguishability of the transaction. */ 756 | secp256k1_scalar_clear(&t_k_scalar); 757 | if (label_tweak != NULL) { 758 | + int tweak_overflow; 759 | secp256k1_scalar label_tweak_scalar; 760 | + secp256k1_scalar_set_b32(&label_tweak_scalar, label_tweak, &tweak_overflow);
fametrano commented at 6:07 AM on October 23, 2026:Declassify
tweak_overflowbefore it is branched on, as is done forvalid_scan_key.secp256k1_scalar_set_b32(&label_tweak_scalar, label_tweak, &tweak_overflow); secp256k1_declassify(ctx, &tweak_overflow, sizeof(tweak_overflow));
theStack commented at 9:42 AM on October 23, 2026:Would prefer to do this in a follow-up PR, where we can discuss whether the label-related values need to be treated as secret in the context of the scanning function in the first place (I'd lean towards no, but curious about other opinions).
Yudis-bit commented at 10:19 AM on November 2, 2026:Hm, but the comment right below line 760 explicitly says:
/* Leaking these values would break indistinguishability of the transaction, so clear them. */
and clears
label_tweak_scalar. If leaking it breaks indistinguishability, treating it as public seems inconsistent with the cleanup in this very PR.Also
secp256k1_declassifyis a no-op outside valgrind/msan builds anyway, so adding it here avoids leaving a known ctime test failure in the tree.theStack added the label assurance on Oct 23, 2026theStack added the label tweak/refactor on Oct 23, 202695552e1654silentpayments: reject label tweaks that overflow the group order
Previously, a label tweak returned by the `label_lookup` callback that is not smaller than the group order was silently reduced modulo the group order. Such a tweak can never be produced by `secp256k1_silentpayments_recipient_label_create`, so it indicates a corrupted or improperly constructed label cache, i.e., a bug on the caller side rather than invalid data from an untrusted source. Treat this as an API misuse (similar to the magic byte checks on the `prevouts_summary` argument), and add a test. The assignment of the found output is moved after the label tweak check, so that the found_outputs[k] entry is left untouched if the scan is aborted due to an invalid label tweak.
in include/secp256k1_silentpayments.h:1 in b5fb2adf09 outdated
real-or-random commented at 7:59 AM on November 2, 2026:Do we need the docs changes at all? If I understand correctly, the way the API is designed is that label tweaks are opaque to the caller, and they should use
secp256k1_silentpayments_recipient_label_createto create one. Talking about scalars will only confuse the user.
theStack commented at 10:19 AM on November 2, 2026:Good point, dropped the docs changes, adapted the commit message and PR description accordingly. Mentioning low-level details like scalars and group order for user types in the API docs seems counter-productive indeed.
theStack force-pushed on Nov 2, 2026fametrano commented at 4:58 PM on November 2, 2026: contributortACK 95552e1654bfd3d34facfcf64e7ca3b0a52594c0
Since my test of b5fb2adf,
git range-diffshows only the header doc changes dropped, from the commit and its message; the code and the test are unchanged. On Linux aarch64 (gcc 14.2, valgrind 3.24, no asm),make checkpasses andctime_testsgives 0 errors under valgrind, on the head and on the head merged with master a3a5cd4 (patch, commands and logs).The inline thread mentions a ctime failure left in the tree. The only one I know of is the one I reported in that thread, from a scan I added, and that scan is not in the tree.
ctime_testsgives 0 errors with or without thesecp256k1_declassifyline, because its scan passes no label lookup, which its comment says is sufficient, and never reachesif (tweak_overflow). With my scan, which finds a labeled output, valgrind reports 0 errors with only the scan key marked secret. With the label tweak marked secret too, it reports 1 error atif (tweak_overflow). The declassify line removes that error, and the merge base gives 0 errors on the same test. So this only matters if the label tweak counts as secret, and even then the branch reveals only that a caller passed an invalid tweak, whichrecipient_label_createnever returns. It does not block the PR.That question, including Yudis-bit's point about clearing the tweak, stays open for the follow-up theStack proposed. If the answer is that the tweak is secret, the fix is my one-line suggestion, and my scan can go into
ctime_testswith it.real-or-random approvedreal-or-random commented at 9:36 AM on November 3, 2026: contributorutACK 95552e1654bfd3d34facfcf64e7ca3b0a52594c0
Yes, we can talk about whether to declassify or not in a follow-up
real-or-random merged this on Nov 3, 2026real-or-random closed this on Nov 3, 2026theStack deleted the branch on Nov 3, 2026
This is a metadata mirror of the GitHub repository bitcoin-core/secp256k1. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-11-14 00:15 UTC
More mirrored repositories can be found on mirror.b10c.me