230 lines
6.2 KiB
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
|
|
}
|