Skip to content

feat(sdk-core): add signRecoveryMpcV2 for EdDSA MPCv2 offline signing#9024

Merged
vibhavgo merged 1 commit into
masterfrom
WCI-397/sdk-core
Jun 18, 2026
Merged

feat(sdk-core): add signRecoveryMpcV2 for EdDSA MPCv2 offline signing#9024
vibhavgo merged 1 commit into
masterfrom
WCI-397/sdk-core

Conversation

@vibhavgo

@vibhavgo vibhavgo commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Description

Adds signRecoveryMpcV2 to modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsaMPCv2.ts as a standalone export for EdDSA MPCv2 recovery signing flows. The function drives the MPS DSG protocol locally to completion using user and backup key shares (obtained from getEddsaMPCv2RecoveryKeyShares), then verifies the resulting 64-byte Ed25519 signature against the child public key derived via deriveUnhardenedMps. Mirrors the existing ECDSA signRecoveryMpcV2 pattern.

Issue Number

WCI-397

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

Unit tests added in modules/sdk-core/test/unit/bitgo/utils/tss/eddsa/eddsaMPCv2.ts under describe('signRecoveryMpcV2'):

  • Valid path — runs full 2-party MPS signing with real DKG key shares, asserts the returned Buffer is 64 bytes and verifies with nacl.sign.detached.verify against deriveUnhardenedMps
  • Tampered message — signs one message, verifies against a different message, asserts nacl verification returns false
  • Wrong commonKeyChain — passes a key chain from a different DKG session, asserts the function throws "EdDSA MPCv2 recovery signature verification failed"

Run with:

yarn run unit-test --scope @bitgo/sdk-core -- -- --grep "signRecoveryMpcV2"

@vibhavgo vibhavgo requested review from a team as code owners June 15, 2026 11:39
@linear-code

linear-code Bot commented Jun 15, 2026

Copy link
Copy Markdown

WCI-397

@vibhavgo vibhavgo requested a review from Marzooqa June 15, 2026 11:39
lcovar
lcovar previously approved these changes Jun 15, 2026
@vibhavgo vibhavgo force-pushed the WCI-396/sdk-core branch 2 times, most recently from d225f17 to 95b7c38 Compare June 15, 2026 16:41
@vibhavgo vibhavgo force-pushed the WCI-397/sdk-core branch 4 times, most recently from 7b079de to 7d7406e Compare June 15, 2026 17:28
Base automatically changed from WCI-396/sdk-core to master June 16, 2026 11:39
@vibhavgo vibhavgo dismissed lcovar’s stale review June 16, 2026 11:39

The base branch was changed.

@Marzooqa

Copy link
Copy Markdown
Contributor

@claude

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Marzooqa's task —— View job


PR Review Analysis

I've completed my review of this PR that adds signRecoveryMpcV2 for EdDSA MPCv2 offline signing. Here's my comprehensive analysis:

Todo List

  • Read and review the implementation file
  • Read and review the test file
  • Analyze code quality and security
  • Check adherence to existing patterns
  • Provide comprehensive review feedback

Review Summary ✅

This is a well-implemented feature that adds EdDSA MPCv2 recovery signing functionality. The code follows existing patterns and includes comprehensive tests.

Implementation Review

signRecoveryEddsaMPCv2 function (modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsaMPCv2.ts:1003-1037):

Strengths:

  • Clear, comprehensive JSDoc documentation
  • Follows existing naming conventions (mirrors ECDSA signRecoveryMpcV2)
  • Proper error handling with descriptive error messages
  • Built-in signature verification for safety
  • Clean separation of concerns (signing → verification)
  • Appropriate type safety with Buffer parameters

Security:

  • Signature verification prevents invalid signatures from being returned
  • Uses established cryptographic libraries (nacl, @bitgo/sdk-lib-mpc)
  • Proper handling of key material (no logging/exposure)
  • Defensive programming with verification step

Test Coverage Review

Test file (modules/sdk-core/test/unit/bitgo/utils/tss/eddsa/eddsaMPCv2.ts:1772-1841):

Comprehensive test coverage:

  • Valid path test: Full 2-party MPS signing with real DKG key shares
  • Tampered message test: Verifies different message fails verification
  • Wrong key chain test: Ensures different DKG session keys fail appropriately

Test quality:

  • Uses real cryptographic operations (not mocks)
  • Proper assertions on signature length (64 bytes)
  • Tests both positive and negative cases
  • Good error message validation

Code Quality Assessment

Follows established patterns:

  • Consistent with existing getEddsaMpcV2RecoveryKeySharesFromReducedKey function
  • Matches parameter naming and documentation style
  • Proper import organization
  • Export pattern consistent with other utility functions

Implementation details:

  • Efficient use of MPSUtil.executeTillRound(3, ...) for 2-party signing
  • Correct key derivation using deriveUnhardenedMps
  • Proper Buffer/Uint8Array conversions for nacl compatibility
  • Clean function signature with all necessary parameters

Minor Observations

  1. Function placement: The new function is appropriately placed at the end of the file after related recovery functions
  2. Import additions: New imports (nacl, deriveUnhardenedMps, MPSUtil) are properly added
  3. No breaking changes: This is a pure addition with no modifications to existing functionality

Recommendations

The implementation looks solid overall. A few minor suggestions for future consideration:

  1. Consider parameter validation: While not critical, adding basic validation (e.g., Buffer length checks) could improve robustness
  2. Performance note: The verification step adds overhead but provides valuable safety - this tradeoff seems appropriate for recovery scenarios

Final Assessment

This PR successfully adds the requested EdDSA MPCv2 recovery signing functionality with:

  • ✅ Correct cryptographic implementation
  • ✅ Appropriate security measures
  • ✅ Comprehensive test coverage
  • ✅ Good code quality and documentation
  • ✅ Consistent with existing patterns

Recommendation: Approve - This is a well-implemented feature that adds valuable recovery functionality while maintaining high code quality standards.


Job run: https://github.com/BitGo/BitGoJS/actions/runs/12345

Comment thread modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsaMPCv2.ts Outdated
Ticket: WCI-397

Implements local 2-party MPS signing for EdDSA MPCv2 recovery flows, mirroring
the existing ECDSA signRecoveryMpcV2 pattern. The function runs the MPS DSG
protocol to round 3 using user and backup key shares, then verifies the resulting
64-byte Ed25519 signature against the child public key derived via deriveUnhardenedMps.

- export `signRecoveryMpcV2` from eddsaMPCv2.ts
  - drives `MPSUtil.executeTillRound(3, ...)` locally with user + backup DSG instances
  - derives the child public key via `deriveUnhardenedMps(commonKeyChain, derivationPath)`
  - verifies the signature with `nacl.sign.detached.verify`; throws on mismatch
- add unit tests for `signRecoveryMpcV2`
  - valid path: 64-byte signature verifies against derived public key
  - tampered message: signature does not verify against a different message
  - wrong commonKeyChain: throws "EdDSA MPCv2 recovery signature verification failed"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@vibhavgo vibhavgo requested a review from Marzooqa June 17, 2026 07:56
@Marzooqa

Copy link
Copy Markdown
Contributor

@claude

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Marzooqa's task —— View job


PR Review Update

Todo List

  • Read current implementation files
  • Review outstanding questions from code review
  • Analyze any recent changes
  • Provide updated feedback

Review Analysis

I've reviewed the current implementation and can address the review concerns:

Addressing Review Comments

Marzooqa's question: "Should this be a try catch?"

Looking at the implementation (modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsaMPCv2.ts:1027), the ed25519.verify() call and subsequent error throwing is appropriately handled:

const verified = ed25519.verify(new Uint8Array(signature), new Uint8Array(message), new Uint8Array(publicKeyBytes));
if (!verified) {
  throw new Error('EdDSA MPCv2 recovery signature verification failed');
}

Answer: No try-catch needed here. The current approach is correct because:

  1. ed25519.verify() is deterministic - it returns boolean, doesn't throw exceptions for normal inputs
  2. Explicit error handling - The function intentionally throws when verification fails, which is the desired behavior for a recovery signing function
  3. Matches existing patterns - This follows the same error handling pattern as other cryptographic functions in the codebase
  4. Clear failure mode - Callers expect this function to throw on verification failure, making the error explicit and traceable

If MPSUtil.executeTillRound() or deriveUnhardenedMps() could throw, those would be the places to consider try-catch, but the verification step should remain as-is.

Code Quality Assessment

Implementation looks solid:

  • Uses @noble/curves/ed25519 instead of nacl (addressing vibhavgo's "Replacing with noble curve" comment)
  • Proper cryptographic verification before returning signatures
  • Clear error messages and appropriate error handling
  • Comprehensive test coverage with edge cases

Security considerations:

  • Built-in signature verification prevents invalid signatures
  • No key material logging or exposure
  • Proper Buffer/Uint8Array conversions for noble curve compatibility

Updated Recommendation

The implementation is well-designed and the current error handling approach is appropriate. No try-catch wrapper is needed around the verification logic.


PR: #9024 | Branch: WCI-397/sdk-core

@vibhavgo vibhavgo merged commit b16a564 into master Jun 18, 2026
23 checks passed
@vibhavgo vibhavgo deleted the WCI-397/sdk-core branch June 18, 2026 05:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants