hardening(frost): zeroize DKG FFI secret buffers (parity with Sign path)#4128
Merged
mswilkison merged 1 commit intoJul 2, 2026
Conversation
The native tbtc-signer Sign path scrubs its Go-heap FFI transport buffers that carry secret material (via `defer zeroBytes(...)` on the request/ response/nonces slices), but the DKG path did not, leaving long-term share and DKG secret material resident in the Go heap after use. This closes that DKG<->Sign zeroization inconsistency. The DKG engine methods build a Go-heap request payload (JSON), hand a C copy to the Rust FFI via C.CBytes, and receive the response as a fresh Go slice via C.GoBytes. callBuildTaggedTBTCSignerOperation already scrubs and frees the C-heap request copy, and the Rust side frees the C response buffer, but the Go-side request/response slices were never wiped. Mirror the Sign path exactly by deferring zeroBytes on the secret-bearing Go buffers, so a mid-function or error return still wipes: - Part1: response (round-1 secret package / private polynomial coeffs). - Part2: request (round-1 secret package) and response (round-2 secret package + per-recipient round-2 secret shares). - Part3: request (round-2 secret package + received secret shares) and response (final key package / long-term signing share). - RunDKGWithSeed: request (DKG seed that deterministically reconstructs the group secret); its response is public metadata only. Public-only buffers are left untouched (RunDKG request/response, Part1 request). The defers run after the decoders evaluate the return value, and the decoders return freshly hex-decoded copies, so wiping the transport buffers never corrupts the returned secrets. cgo-safe: the Go slices are independent of the C copies, so zeroing them after the call returns neither races the C side nor risks a double-free. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
275b508
into
feat/frost-schnorr-migration-scaffold
17 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Finding
The native tbtc-signer Sign path scrubs its Go-heap FFI transport buffers that carry secret material —
defer zeroBytes(...)on the request payload, response payload, and nonces slices (native_frost_engine_tbtc_signer_registration_frost_native.go). The DKG path in the same file did not, leaving long-term share / DKG secret material resident in the Go heap after use. This is a memory-hygiene inconsistency: DKG secrets (private polynomial coefficients, per-recipient secret shares, and the final long-term signing share) linger in reclaimable-but-unwiped Go heap buffers, whereas the equivalent Sign secrets are wiped.Sites zeroed
Each DKG engine method marshals a Go-heap JSON request, hands a copy to Rust via
C.CBytes, and receives the response as a fresh Go slice viaC.GoBytes. Only the genuinely secret-bearing Go-side buffers are wiped (public-only buffers are left untouched):Part1(~L718)Part2(~L737)Part2(~L746)Part3(~L768)Part3(~L777)RunDKGWithSeed(~L686)Left untouched because they carry no secret:
RunDKGrequest/response (participant public keys + metadata),RunDKGWithSeedresponse (public metadata only),Part1request (participant id + signer counts).How it mirrors the Sign path
The Sign path uses the package-local
zeroBytes(data []byte)helper (native_frost_engine_frost_native.go:59) viadefer:GenerateNoncesAndCommitments:defer zeroBytes(responsePayload)(response carries one-time nonces).Sign:defer zeroBytes(noncesData)anddefer zeroBytes(requestPayload).This change reuses the same helper and the same
deferplacement (right after a secret request is built / a secret response is received), so a mid-function or error return still wipes. No new/divergent mechanism is introduced.cgo-safety reasoning
callBuildTaggedTBTCSignerOperationalreadyC.CBytes-copies the request to the C heap and, on defer,zeroBytes+C.frees that C copy. The Go-side request slice is a separatejson.Marshalallocation, so zeroing it after the call returns neither races the C side nor risks a double-free.C.GoBytescopy; the C-side response buffer is freed separately bytbtc_signer_free_buffer. Wiping the Go copy is independent and safe.zeroBytesruns after the decoder evaluates the return value, and the decoders return freshly hex-decoded copies (independent of the transport buffer), so wiping never corrupts the returned secret. Identical ordering to the existingGenerateNoncesAndCommitments.Validation
gofmt -lclean on the touched file.go vet -tags "frost_native frost_tbtc_signer" ./pkg/frost/signing/clean.go build -tags "frost_native frost_tbtc_signer" ./pkg/frost/...succeeds (cgo path uses runtimedlopen, so it compiles the touched file without the Rust lib present).go build -tags "frost_roast_retry" ./pkg/frost/...succeeds (non-cgo compile check).🤖 Generated with Claude Code