rwadurian/backend/mpc-system/services/server-party/application/use_cases/participate_signing.go

230 lines
6.2 KiB
Go

package use_cases
import (
"context"
"errors"
"math/big"
"time"
"github.com/google/uuid"
"github.com/rwadurian/mpc-system/pkg/crypto"
"github.com/rwadurian/mpc-system/pkg/logger"
"github.com/rwadurian/mpc-system/services/server-party/domain/repositories"
"go.uber.org/zap"
)
var (
ErrSigningFailed = errors.New("signing failed")
ErrSigningTimeout = errors.New("signing timeout")
ErrKeyShareNotFound = errors.New("key share not found")
ErrInvalidSignSession = errors.New("invalid sign session")
)
// ParticipateSigningInput contains input for signing participation
type ParticipateSigningInput struct {
SessionID uuid.UUID
PartyID string
JoinToken string
MessageHash []byte
}
// ParticipateSigningOutput contains output from signing participation
type ParticipateSigningOutput struct {
Success bool
Signature []byte
R *big.Int
S *big.Int
}
// ParticipateSigningUseCase handles signing participation
type ParticipateSigningUseCase struct {
keyShareRepo repositories.KeyShareRepository
sessionClient SessionCoordinatorClient
messageRouter MessageRouterClient
cryptoService *crypto.CryptoService
}
// NewParticipateSigningUseCase creates a new participate signing use case
func NewParticipateSigningUseCase(
keyShareRepo repositories.KeyShareRepository,
sessionClient SessionCoordinatorClient,
messageRouter MessageRouterClient,
cryptoService *crypto.CryptoService,
) *ParticipateSigningUseCase {
return &ParticipateSigningUseCase{
keyShareRepo: keyShareRepo,
sessionClient: sessionClient,
messageRouter: messageRouter,
cryptoService: cryptoService,
}
}
// Execute participates in a signing session
func (uc *ParticipateSigningUseCase) Execute(
ctx context.Context,
input ParticipateSigningInput,
) (*ParticipateSigningOutput, error) {
// 1. Join session via coordinator
sessionInfo, err := uc.sessionClient.JoinSession(ctx, input.SessionID, input.PartyID, input.JoinToken)
if err != nil {
return nil, err
}
if sessionInfo.SessionType != "sign" {
return nil, ErrInvalidSignSession
}
// 2. Load key share for this party
// In a real implementation, we'd need to identify which keygen session this signing session relates to
keyShares, err := uc.keyShareRepo.ListByParty(ctx, input.PartyID)
if err != nil || len(keyShares) == 0 {
return nil, ErrKeyShareNotFound
}
// Use the most recent key share (in production, would match by public key or session reference)
keyShare := keyShares[len(keyShares)-1]
// 3. Decrypt share data
shareData, err := uc.cryptoService.DecryptShare(keyShare.ShareData, input.PartyID)
if err != nil {
return nil, err
}
// 4. Find self in participants
var selfIndex int
for _, p := range sessionInfo.Participants {
if p.PartyID == input.PartyID {
selfIndex = p.PartyIndex
break
}
}
// 5. Subscribe to messages
msgChan, err := uc.messageRouter.SubscribeMessages(ctx, input.SessionID, input.PartyID)
if err != nil {
return nil, err
}
// 6. Run TSS Signing protocol
signature, r, s, err := uc.runSigningProtocol(
ctx,
input.SessionID,
input.PartyID,
selfIndex,
sessionInfo.Participants,
sessionInfo.ThresholdT,
shareData,
input.MessageHash,
msgChan,
)
if err != nil {
return nil, err
}
// 7. Update key share last used
keyShare.MarkUsed()
if err := uc.keyShareRepo.Update(ctx, keyShare); err != nil {
logger.Warn("failed to update key share last used", zap.Error(err))
}
// 8. Report completion to coordinator
if err := uc.sessionClient.ReportCompletion(ctx, input.SessionID, input.PartyID, signature); err != nil {
logger.Error("failed to report signing completion", zap.Error(err))
}
return &ParticipateSigningOutput{
Success: true,
Signature: signature,
R: r,
S: s,
}, nil
}
// runSigningProtocol runs the TSS signing protocol
// This is a placeholder implementation
func (uc *ParticipateSigningUseCase) runSigningProtocol(
ctx context.Context,
sessionID uuid.UUID,
partyID string,
selfIndex int,
participants []ParticipantInfo,
t int,
shareData []byte,
messageHash []byte,
msgChan <-chan *MPCMessage,
) ([]byte, *big.Int, *big.Int, error) {
/*
Real implementation would:
1. Deserialize LocalPartySaveData from shareData
2. Create tss.PartyID list
3. Create tss.Parameters
4. Create signing.LocalParty with message hash
5. Handle outgoing messages via messageRouter
6. Handle incoming messages from msgChan
7. Wait for signing completion
8. Return signature (R, S)
Example with tss-lib:
var saveData keygen.LocalPartySaveData
saveData.UnmarshalBinary(shareData)
parties := make([]*tss.PartyID, len(participants))
for i, p := range participants {
parties[i] = tss.NewPartyID(p.PartyID, p.PartyID, big.NewInt(int64(p.PartyIndex)))
}
selfPartyID := parties[selfIndex]
tssCtx := tss.NewPeerContext(parties)
params := tss.NewParameters(tss.S256(), tssCtx, selfPartyID, len(participants), t)
outCh := make(chan tss.Message, len(participants)*10)
endCh := make(chan *common.SignatureData, 1)
msgHash := new(big.Int).SetBytes(messageHash)
party := signing.NewLocalParty(msgHash, params, saveData, outCh, endCh)
go handleOutgoingMessages(ctx, sessionID, partyID, outCh)
go handleIncomingMessages(ctx, party, msgChan)
party.Start()
select {
case signData := <-endCh:
signature := append(signData.R, signData.S...)
return signature, signData.R, signData.S, nil
case <-time.After(5*time.Minute):
return nil, nil, nil, ErrSigningTimeout
}
*/
// Placeholder: Generate mock signature for demonstration
logger.Info("Running signing protocol (placeholder)",
zap.String("session_id", sessionID.String()),
zap.String("party_id", partyID),
zap.Int("self_index", selfIndex),
zap.Int("t", t),
zap.Int("message_hash_len", len(messageHash)))
// Simulate signing delay
select {
case <-ctx.Done():
return nil, nil, nil, ctx.Err()
case <-time.After(1 * time.Second):
}
// Generate placeholder signature (R || S, each 32 bytes)
r := new(big.Int).SetBytes(messageHash[:16])
s := new(big.Int).SetBytes(messageHash[16:])
signature := make([]byte, 64)
rBytes := r.Bytes()
sBytes := s.Bytes()
// Pad to 32 bytes each
copy(signature[32-len(rBytes):32], rBytes)
copy(signature[64-len(sBytes):64], sBytes)
return signature, r, s, nil
}