Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1c0d65b
fix typo
nann-cheng Jul 30, 2025
73825db
Merge branch 'fystack:master' into master
nann-cheng Jul 31, 2025
9e5bc4a
draft impl for diffie hellman exchange
nann-cheng Aug 4, 2025
d13261f
Merge branch 'master' of https://github.com/nann-cheng/mpcium
nann-cheng Aug 4, 2025
f7f6181
New Feature: add p2p channel among nodes, enabling authenticated encr…
nann-cheng Aug 6, 2025
2ace0d7
Merge branch 'fystack:master' into master
nann-cheng Aug 6, 2025
57c51dd
decrease delay
nann-cheng Aug 6, 2025
6d908f7
suppress warning
nann-cheng Aug 6, 2025
887a42e
Merge branch 'master' of https://github.com/nann-cheng/mpcium
nann-cheng Aug 6, 2025
071ac87
increase delay for starting client
nann-cheng Aug 6, 2025
fa21efb
increase delay in test case, supporess github security warning
nann-cheng Aug 6, 2025
0f69eaf
fix p2p feature correctness
nann-cheng Aug 10, 2025
94838c3
fix p2p feature
nann-cheng Aug 10, 2025
608ebfb
merge master
nann-cheng Aug 10, 2025
3eaf6c9
fix format error
nann-cheng Aug 10, 2025
03dd98c
revert old testing config
nann-cheng Aug 10, 2025
6c43ac2
correct log & remove useless comment
nann-cheng Aug 11, 2025
2dd7f6c
avoid p2p message via nats message layer
nann-cheng Aug 11, 2025
cb91e65
gracely merge master
nann-cheng Aug 12, 2025
f9d0a68
change to modern x25519 curve for ecdh
nann-cheng Aug 13, 2025
5ee33fc
Minor cleanup
anhthii Aug 13, 2025
318762e
Consistent naming convention Id -> ID
anhthii Aug 13, 2025
345f92d
Wait for ECDH session to complete before starting consumers
anhthii Aug 13, 2025
2ea0d32
Code refactoring, better naming convention
anhthii Aug 13, 2025
fc8bab7
Refactor aes encryption
anhthii Aug 13, 2025
bf76a20
Error handling for ecdh session
anhthii Aug 13, 2025
1e3021d
Refactor session
anhthii Aug 13, 2025
538fabe
Merge pull request #1 from nann-cheng/refactoring
anhthii Aug 13, 2025
0049c7c
Fix security warning on AES encryption
anhthii Aug 13, 2025
170245c
Update ci/cd
anhthii Aug 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 24 additions & 62 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
pull_request:
branches: ["*"]

env:
GO_VERSION: "1.24"

jobs:
test:
runs-on: ubuntu-latest
Expand All @@ -15,19 +18,10 @@ jobs:
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: "1.23"

- name: Cache Go modules
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
go-version: ${{ env.GO_VERSION }}
cache: true

- name: Install dependencies
run: go mod download
Expand All @@ -50,9 +44,13 @@ jobs:
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: "1.23"
go-version: ${{ env.GO_VERSION }}
cache: true

- name: Clean Go build cache
run: go clean -cache -modcache

- name: Run golangci-lint
uses: golangci/golangci-lint-action@v3
Expand All @@ -74,19 +72,10 @@ jobs:
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: "1.23"

- name: Cache Go modules
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
go-version: ${{ env.GO_VERSION }}
cache: true

- name: Install dependencies
run: go mod download
Expand Down Expand Up @@ -153,19 +142,10 @@ jobs:
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: "1.23"

- name: Cache Go modules
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
go-version: ${{ env.GO_VERSION }}
cache: true

- name: Install dependencies
run: go mod download
Expand Down Expand Up @@ -200,19 +180,10 @@ jobs:
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: "1.23"

- name: Cache Go modules
uses: actions/cache@v3
uses: actions/setup-go@v5
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
go-version: ${{ env.GO_VERSION }}
cache: true

- name: Install dependencies
run: go mod download
Expand Down Expand Up @@ -293,19 +264,10 @@ jobs:
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: "1.23"

- name: Cache Go modules
uses: actions/cache@v3
uses: actions/setup-go@v5
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
go-version: ${{ env.GO_VERSION }}
cache: true

- name: Build mpcium
run: go build -v ./cmd/mpcium
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
branches: [master]

env:
GO_VERSION: "1.23"
GO_VERSION: "1.24"
CGO_ENABLED: 0
DOCKER_BUILDKIT: 1
GO_BUILD_FLAGS: -trimpath -ldflags="-s -w"
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@ e2e/coverage.html
e2e/logs/
# Generated config file (template is tracked)
e2e/config.test.yaml
node0
node1
node2
config.yaml
37 changes: 33 additions & 4 deletions cmd/mpcium/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func runNode(ctx context.Context, c *cli.Command) error {
reshareResultQueue := mqManager.NewMessageQueue("mpc_reshare_result")
defer reshareResultQueue.Close()

logger.Info("Node is running", "peerID", nodeID, "name", nodeName)
logger.Info("Node is running", "ID", nodeID, "name", nodeName)

peerNodeIDs := GetPeerIDs(peers)
peerRegistry := mpc.NewRegistry(nodeID, peerNodeIDs, consulClient.KV())
Expand All @@ -176,6 +176,9 @@ func runNode(ctx context.Context, c *cli.Command) error {
)
defer mpcNode.Close()

// ECDH session for DH key exchange
ecdhSession := mpcNode.GetECDHSession()

eventConsumer := eventconsumer.NewEventConsumer(
mpcNode,
pubsub,
Expand All @@ -202,8 +205,15 @@ func runNode(ctx context.Context, c *cli.Command) error {
logger.Error("Failed to mark peer registry as ready", err)
}
logger.Info("[READY] Node is ready", "nodeID", nodeID)

logger.Info("Waiting for ECDH key exchange to complete...", "nodeID", nodeID)
if err := ecdhSession.WaitForExchangeComplete(); err != nil {
logger.Fatal("ECDH exchange failed", err)
}

logger.Info("ECDH key exchange completed successfully, starting consumers...", "nodeID", nodeID)
appContext, cancel := context.WithCancel(context.Background())
// Setup signal handling to cancel context on termination signals.
//Setup signal handling to cancel context on termination signals.
go func() {
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
Expand All @@ -218,10 +228,14 @@ func runNode(ctx context.Context, c *cli.Command) error {
if err := signingConsumer.Close(); err != nil {
logger.Error("Failed to close signing consumer", err)
}

if err := ecdhSession.Close(); err != nil {
logger.Error("Failed to close ECDH session", err)
}
}()

var wg sync.WaitGroup
errChan := make(chan error, 2)
errChan := make(chan error, 3)

wg.Add(1)
go func() {
Expand All @@ -245,19 +259,34 @@ func runNode(ctx context.Context, c *cli.Command) error {
logger.Info("Signing consumer finished successfully")
}()

go func() {
for {
select {
case <-appContext.Done():
return
case err := <-ecdhSession.ErrChan():
if err != nil {
logger.Error("ECDH session error", err)
errChan <- fmt.Errorf("ecdh session error: %w", err)
return
}
}
}
}()

go func() {
wg.Wait()
logger.Info("All consumers have finished")
close(errChan)
}()

for err := range errChan {
if err != nil {
logger.Error("Consumer error received", err)
cancel()
return err
}
}

return nil
}

Expand Down
2 changes: 1 addition & 1 deletion config.yaml.template
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ consul:

mpc_threshold: 2
environment: development
badger_password: "your_badger_password"
badger_password: "F))ysJp?E]ol&I;^"
event_initiator_pubkey: "event_initiator_pubkey"
db_path: "."
backup_enabled: true
Expand Down
6 changes: 3 additions & 3 deletions e2e/reshare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func testKeyGenerationForResharing(t *testing.T, suite *E2ETestSuite) {
require.NoError(t, err, "Failed to setup keygen result listener")

// Add a small delay to ensure the result listener is fully set up
time.Sleep(2 * time.Second)
time.Sleep(10 * time.Second)

// Trigger key generation for all wallets
for _, walletID := range walletIDs {
Expand Down Expand Up @@ -179,7 +179,7 @@ func testResharingAllNodes(t *testing.T, suite *E2ETestSuite) {
require.NoError(t, err, "Failed to setup resharing result listener")

// Wait for listener setup
time.Sleep(2 * time.Second)
time.Sleep(10 * time.Second)

// Test resharing for both key types
for i, walletID := range suite.walletIDs {
Expand Down Expand Up @@ -360,7 +360,7 @@ func testSigningAfterResharing(t *testing.T, suite *E2ETestSuite) {
require.NoError(t, err, "Failed to setup signing result listener")

// Wait for listener setup
time.Sleep(2 * time.Second)
time.Sleep(10 * time.Second)

// Test messages to sign
testMessages := []string{
Expand Down
1 change: 0 additions & 1 deletion e2e/setup_test_identities.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

# E2E Test Identity Setup Script
# This script sets up identities for testing with separate test database paths

set -e

# Number of test nodes
Expand Down
2 changes: 1 addition & 1 deletion e2e/sign_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func testKeyGenerationForSigning(t *testing.T, suite *E2ETestSuite) {
require.NoError(t, err, "Failed to setup keygen result listener")

// Add a small delay to ensure the result listener is fully set up
time.Sleep(2 * time.Second)
time.Sleep(10 * time.Second)

// Trigger key generation for all wallets
for _, walletID := range walletIDs {
Expand Down
6 changes: 3 additions & 3 deletions examples/reshare/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ func main() {

resharingMsg := &types.ResharingMessage{
SessionID: uuid.NewString(),
WalletID: "bf2cc849-8e55-47e4-ab73-e17fb1eb690c",
NodeIDs: []string{"d926fa75-72c7-4538-9052-4a064a84981d", "7b1090cd-ffe3-46ff-8375-594dd3204169"}, // new peer IDs
WalletID: "506d2d40-483a-49f1-93c8-27dd4fe9740c",
NodeIDs: []string{"c95c340e-5a18-472d-b9b0-5ac68218213a", "ac37e85f-caca-4bee-8a3a-49a0fe35abff"}, // new peer IDs

NewThreshold: 2, // t+1 <= len(NodeIDs)
NewThreshold: 1, // t+1 <= len(NodeIDs)
KeyType: types.KeyTypeEd25519,
}
err = mpcClient.Resharing(resharingMsg)
Expand Down
50 changes: 50 additions & 0 deletions pkg/encryption/aes.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"errors"
"fmt"
)

func EncryptAESGCM(plain, key []byte) (ciphertext, nonce []byte, err error) {
Expand Down Expand Up @@ -34,3 +36,51 @@ func DecryptAESGCM(ciphertext, key, nonce []byte) ([]byte, error) {
}
return aead.Open(nil, nonce, ciphertext, nil)
}

// EncryptAESGCMWithNonceEmbed encrypts plaintext and embeds the nonce at the start of the returned slice.
func EncryptAESGCMWithNonceEmbed(plaintext, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("failed to create AES cipher: %w", err)
}

aead, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("failed to create GCM: %w", err)
}

nonce := make([]byte, aead.NonceSize())
if _, err := rand.Read(nonce); err != nil {
return nil, fmt.Errorf("failed to generate nonce: %w", err)
}

ciphertext := aead.Seal(nil, nonce, plaintext, nil)
return append(nonce, ciphertext...), nil
}

// DecryptAESGCMWithNonceEmbed decrypts ciphertext where the nonce is embedded at the start of the slice.
func DecryptAESGCMWithNonceEmbed(data, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("failed to create AES cipher: %w", err)
}
aead, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("failed to create GCM: %w", err)
}

nonceSize := aead.NonceSize()
if len(data) < nonceSize {
return nil, errors.New("ciphertext too short")
}

nonce := data[:nonceSize]
ciphertext := data[nonceSize:]

plaintext, err := aead.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, fmt.Errorf("decryption failed: %w", err)
}

return plaintext, nil
}
Loading
Loading