mirror of https://github.com/certusone/dc4bc.git
Merge pull request #44 from depools/feat/airgapped-state
feat: airgapped state
This commit is contained in:
commit
2f5419ffba
12
Makefile
12
Makefile
|
@ -12,18 +12,18 @@ mocks:
|
|||
|
||||
build-darwin:
|
||||
@echo "Building dc4bc_d..."
|
||||
GOOS=darwin GOARCH=amd64 go build -o dc4bc_d_darwin ./cmd/dc4bc_d/main.go
|
||||
GOOS=darwin GOARCH=amd64 go build -o dc4bc_d_darwin ./cmd/dc4bc_d/
|
||||
@echo "Building dc4bc_cli..."
|
||||
GOOS=darwin GOARCH=amd64 go build -o dc4bc_cli_darwin ./cmd/dc4bc_cli/main.go
|
||||
GOOS=darwin GOARCH=amd64 go build -o dc4bc_cli_darwin ./cmd/dc4bc_cli/
|
||||
@echo "Building dc4bc_airgapped..."
|
||||
GOOS=darwin GOARCH=amd64 go build -o dc4bc_airgapped_darwin ./cmd/airgapped/main.go
|
||||
GOOS=darwin GOARCH=amd64 go build -o dc4bc_airgapped_darwin ./cmd/airgapped/
|
||||
|
||||
build-linux:
|
||||
@echo "Building dc4bc_d..."
|
||||
GOOS=linux GOARCH=amd64 go build -o dc4bc_d_linux ./cmd/dc4bc_d/main.go
|
||||
GOOS=linux GOARCH=amd64 go build -o dc4bc_d_linux ./cmd/dc4bc_d/
|
||||
@echo "Building dc4bc_cli..."
|
||||
GOOS=linux GOARCH=amd64 go build -o dc4bc_cli_linux ./cmd/dc4bc_cli/main.go
|
||||
GOOS=linux GOARCH=amd64 go build -o dc4bc_cli_linux ./cmd/dc4bc_cli/
|
||||
@echo "Building dc4bc_airgapped..."
|
||||
GOOS=linux GOARCH=amd64 go build -o dc4bc_airgapped_linux ./cmd/airgapped/main.go
|
||||
GOOS=linux GOARCH=amd64 go build -o dc4bc_airgapped_linux ./cmd/airgapped/
|
||||
|
||||
.PHONY: mocks
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package airgapped
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
@ -20,15 +19,12 @@ import (
|
|||
"github.com/depools/dc4bc/fsm/state_machines/signing_proposal_fsm"
|
||||
"github.com/depools/dc4bc/fsm/types/requests"
|
||||
"github.com/depools/dc4bc/qr"
|
||||
bls12381 "github.com/depools/kyber-bls12381"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
)
|
||||
|
||||
const (
|
||||
resultQRFolder = "result_qr_codes"
|
||||
pubKeyDBKey = "public_key"
|
||||
privateKeyDBKey = "private_key"
|
||||
saltKey = "salt_key"
|
||||
resultQRFolder = "result_qr_codes"
|
||||
seedSize = 32
|
||||
)
|
||||
|
||||
type AirgappedMachine struct {
|
||||
|
@ -40,7 +36,8 @@ type AirgappedMachine struct {
|
|||
encryptionKey []byte
|
||||
pubKey kyber.Point
|
||||
secKey kyber.Scalar
|
||||
suite vss.Suite
|
||||
baseSuite vss.Suite
|
||||
baseSeed []byte
|
||||
|
||||
db *leveldb.DB
|
||||
}
|
||||
|
@ -61,12 +58,25 @@ func NewAirgappedMachine(dbPath string) (*AirgappedMachine, error) {
|
|||
qrProcessor: qr.NewCameraProcessor(),
|
||||
}
|
||||
|
||||
am.suite = bls12381.NewBLS12381Suite()
|
||||
|
||||
if am.db, err = leveldb.OpenFile(dbPath, nil); err != nil {
|
||||
return nil, fmt.Errorf("failed to open db file %s for keys: %w", dbPath, err)
|
||||
}
|
||||
|
||||
if err := am.loadBaseSeed(); err != nil {
|
||||
return nil, fmt.Errorf("failed to loadBaseSeed: %w", err)
|
||||
}
|
||||
|
||||
if _, err = am.db.Get([]byte(operationsLogDBKey), nil); err != nil {
|
||||
if err == leveldb.ErrNotFound {
|
||||
operationsLogBz, _ := json.Marshal(RoundOperationLog{})
|
||||
if err := am.db.Put([]byte(operationsLogDBKey), operationsLogBz, nil); err != nil {
|
||||
return nil, fmt.Errorf("failed to init Operation log: %w", err)
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("failed to init Operation log (fatal): %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return am, nil
|
||||
}
|
||||
|
||||
|
@ -78,8 +88,8 @@ func (am *AirgappedMachine) InitKeys() error {
|
|||
}
|
||||
// if keys were not generated yet
|
||||
if err == leveldb.ErrNotFound {
|
||||
am.secKey = am.suite.Scalar().Pick(am.suite.RandomStream())
|
||||
am.pubKey = am.suite.Point().Mul(am.secKey, nil)
|
||||
am.secKey = am.baseSuite.Scalar().Pick(am.baseSuite.RandomStream())
|
||||
am.pubKey = am.baseSuite.Point().Mul(am.secKey, nil)
|
||||
return am.SaveKeysToDB()
|
||||
}
|
||||
|
||||
|
@ -107,100 +117,27 @@ func (am *AirgappedMachine) DropSensitiveData() {
|
|||
am.encryptionKey = nil
|
||||
}
|
||||
|
||||
// LoadKeysFromDB load DKG keys from LevelDB
|
||||
func (am *AirgappedMachine) LoadKeysFromDB() error {
|
||||
pubKeyBz, err := am.db.Get([]byte(pubKeyDBKey), nil)
|
||||
func (am *AirgappedMachine) ReplayOperationsLog(dkgIdentifier string) error {
|
||||
operationsLog, err := am.getOperationsLog(dkgIdentifier)
|
||||
if err != nil {
|
||||
if err == leveldb.ErrNotFound {
|
||||
return err
|
||||
return fmt.Errorf("failed to getOperationsLog: %w", err)
|
||||
}
|
||||
|
||||
for _, operation := range operationsLog {
|
||||
if _, err := am.HandleOperation(operation); err != nil {
|
||||
return fmt.Errorf(
|
||||
"failed to HandleOperation %s (this error is fatal, the state can not be recovered): %w",
|
||||
operation.ID, err)
|
||||
}
|
||||
return fmt.Errorf("failed to get public key from db: %w", err)
|
||||
}
|
||||
|
||||
privateKeyBz, err := am.db.Get([]byte(privateKeyDBKey), nil)
|
||||
if err != nil {
|
||||
if err == leveldb.ErrNotFound {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("failed to get private key from db: %w", err)
|
||||
}
|
||||
log.Println("Successfully replayed Operation log")
|
||||
|
||||
salt, err := am.db.Get([]byte(saltKey), nil)
|
||||
if err != nil {
|
||||
if err == leveldb.ErrNotFound {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("failed to read salt from db: %w", err)
|
||||
}
|
||||
|
||||
decryptedPubKey, err := decrypt(am.encryptionKey, salt, pubKeyBz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
decryptedPrivateKey, err := decrypt(am.encryptionKey, salt, privateKeyBz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
am.pubKey = am.suite.Point()
|
||||
if err = am.pubKey.UnmarshalBinary(decryptedPubKey); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal public key: %w", err)
|
||||
}
|
||||
|
||||
am.secKey = am.suite.Scalar()
|
||||
if err = am.secKey.UnmarshalBinary(decryptedPrivateKey); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal private key: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SaveKeysToDB save DKG keys to LevelDB
|
||||
func (am *AirgappedMachine) SaveKeysToDB() error {
|
||||
pubKeyBz, err := am.pubKey.MarshalBinary()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal pub key: %w", err)
|
||||
}
|
||||
privateKeyBz, err := am.secKey.MarshalBinary()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal private key: %w", err)
|
||||
}
|
||||
|
||||
salt := make([]byte, 32)
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
return fmt.Errorf("failed to generate salt: %w", err)
|
||||
}
|
||||
|
||||
encryptedPubKey, err := encrypt(am.encryptionKey, salt, pubKeyBz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encryptedPrivateKey, err := encrypt(am.encryptionKey, salt, privateKeyBz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx, err := am.db.OpenTransaction()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open transcation for db: %w", err)
|
||||
}
|
||||
defer tx.Discard()
|
||||
|
||||
if err = tx.Put([]byte(pubKeyDBKey), encryptedPubKey, nil); err != nil {
|
||||
return fmt.Errorf("failed to put pub key into db: %w", err)
|
||||
}
|
||||
|
||||
if err = tx.Put([]byte(privateKeyDBKey), encryptedPrivateKey, nil); err != nil {
|
||||
return fmt.Errorf("failed to put private key into db: %w", err)
|
||||
}
|
||||
|
||||
if err = tx.Put([]byte(saltKey), salt, nil); err != nil {
|
||||
return fmt.Errorf("failed to put salt into db: %w", err)
|
||||
}
|
||||
|
||||
if err = tx.Commit(); err != nil {
|
||||
return fmt.Errorf("failed to commit tx for saving keys into db: %w", err)
|
||||
}
|
||||
return nil
|
||||
func (am *AirgappedMachine) DropOperationsLog(dkgIdentifier string) error {
|
||||
return am.dropRoundOperationLog(dkgIdentifier)
|
||||
}
|
||||
|
||||
// getParticipantID returns our own participant id for the given DKG round
|
||||
|
@ -224,7 +161,7 @@ func (am *AirgappedMachine) encryptDataForParticipant(dkgIdentifier, to string,
|
|||
return nil, fmt.Errorf("failed to get pk for participant %s: %w", to, err)
|
||||
}
|
||||
|
||||
encryptedData, err := ecies.Encrypt(am.suite, pk, data, am.suite.Hash)
|
||||
encryptedData, err := ecies.Encrypt(am.baseSuite, pk, data, am.baseSuite.Hash)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encrypt data: %w", err)
|
||||
}
|
||||
|
@ -233,7 +170,7 @@ func (am *AirgappedMachine) encryptDataForParticipant(dkgIdentifier, to string,
|
|||
|
||||
// decryptDataFromParticipant decrypts the data that was sent to us
|
||||
func (am *AirgappedMachine) decryptDataFromParticipant(data []byte) ([]byte, error) {
|
||||
decryptedData, err := ecies.Decrypt(am.suite, am.secKey, data, am.suite.Hash)
|
||||
decryptedData, err := ecies.Decrypt(am.baseSuite, am.secKey, data, am.baseSuite.Hash)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decrypt data: %w", err)
|
||||
}
|
||||
|
@ -242,6 +179,14 @@ func (am *AirgappedMachine) decryptDataFromParticipant(data []byte) ([]byte, err
|
|||
|
||||
// HandleOperation handles and processes an operation
|
||||
func (am *AirgappedMachine) HandleOperation(operation client.Operation) (client.Operation, error) {
|
||||
if err := am.storeOperation(operation); err != nil {
|
||||
return client.Operation{}, fmt.Errorf("failed to storeOperation: %w", err)
|
||||
}
|
||||
|
||||
return am.handleOperation(operation)
|
||||
}
|
||||
|
||||
func (am *AirgappedMachine) handleOperation(operation client.Operation) (client.Operation, error) {
|
||||
var (
|
||||
err error
|
||||
)
|
||||
|
|
|
@ -9,6 +9,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
client "github.com/depools/dc4bc/client/types"
|
||||
"github.com/depools/dc4bc/fsm/fsm"
|
||||
"github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm"
|
||||
|
@ -310,12 +312,259 @@ func TestAirgappedAllSteps(t *testing.T) {
|
|||
fmt.Println("DKG succeeded, signature recovered")
|
||||
}
|
||||
|
||||
func TestAirgappedMachine_Replay(t *testing.T) {
|
||||
testDir := "/tmp/airgapped_test"
|
||||
nodesCount := 2
|
||||
threshold := 2
|
||||
participants := make([]string, nodesCount)
|
||||
for i := 0; i < nodesCount; i++ {
|
||||
participants[i] = fmt.Sprintf("Participant#%d", i)
|
||||
}
|
||||
|
||||
tr := &Transport{}
|
||||
for i := 0; i < nodesCount; i++ {
|
||||
am, err := NewAirgappedMachine(fmt.Sprintf("%s/%s-%d", testDir, testDB, i))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create airgapped machine: %v", err)
|
||||
}
|
||||
am.SetEncryptionKey([]byte(fmt.Sprintf(testDB+"%d", i)))
|
||||
if err = am.InitKeys(); err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
node := Node{
|
||||
ParticipantID: i,
|
||||
Participant: participants[i],
|
||||
Machine: am,
|
||||
}
|
||||
tr.nodes = append(tr.nodes, &node)
|
||||
}
|
||||
defer os.RemoveAll(testDir)
|
||||
|
||||
var initReq responses.SignatureProposalParticipantInvitationsResponse
|
||||
for _, n := range tr.nodes {
|
||||
pubKey, err := n.Machine.pubKey.MarshalBinary()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to marshal dkg pubkey: %v", err)
|
||||
}
|
||||
entry := &responses.SignatureProposalParticipantInvitationEntry{
|
||||
ParticipantId: n.ParticipantID,
|
||||
Addr: n.Participant,
|
||||
Threshold: threshold,
|
||||
DkgPubKey: pubKey,
|
||||
}
|
||||
initReq = append(initReq, entry)
|
||||
}
|
||||
op := createOperation(t, string(signature_proposal_fsm.StateAwaitParticipantsConfirmations), "", initReq)
|
||||
runStep(tr, func(n *Node, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
_, err := n.Machine.HandleOperation(op)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: failed to handle operation %s: %v", n.Participant, op.Type, err)
|
||||
}
|
||||
})
|
||||
|
||||
// get commits
|
||||
var getCommitsRequest responses.DKGProposalPubKeysParticipantResponse
|
||||
for _, n := range tr.nodes {
|
||||
pubKey, err := n.Machine.pubKey.MarshalBinary()
|
||||
if err != nil {
|
||||
t.Fatalf("%s: failed to marshal pubkey: %v", n.Participant, err)
|
||||
}
|
||||
entry := &responses.DKGProposalPubKeysParticipantEntry{
|
||||
ParticipantId: n.ParticipantID,
|
||||
Addr: n.Participant,
|
||||
DkgPubKey: pubKey,
|
||||
}
|
||||
getCommitsRequest = append(getCommitsRequest, entry)
|
||||
}
|
||||
op = createOperation(t, string(dkg_proposal_fsm.StateDkgCommitsAwaitConfirmations), "", getCommitsRequest)
|
||||
runStep(tr, func(n *Node, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
operation, err := n.Machine.HandleOperation(op)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: failed to handle operation %s: %v", n.Participant, op.Type, err)
|
||||
}
|
||||
for _, msg := range operation.ResultMsgs {
|
||||
tr.BroadcastMessage(t, msg)
|
||||
}
|
||||
})
|
||||
|
||||
//deals
|
||||
runStep(tr, func(n *Node, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
var payload responses.DKGProposalCommitParticipantResponse
|
||||
for _, req := range n.commits {
|
||||
p := responses.DKGProposalCommitParticipantEntry{
|
||||
ParticipantId: req.ParticipantId,
|
||||
Addr: fmt.Sprintf("Participant#%d", req.ParticipantId),
|
||||
DkgCommit: req.Commit,
|
||||
}
|
||||
payload = append(payload, &p)
|
||||
}
|
||||
op := createOperation(t, string(dkg_proposal_fsm.StateDkgDealsAwaitConfirmations), "", payload)
|
||||
|
||||
operation, err := n.Machine.HandleOperation(op)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: failed to handle operation %s: %v", n.Participant, op.Type, err)
|
||||
}
|
||||
for _, msg := range operation.ResultMsgs {
|
||||
tr.BroadcastMessage(t, msg)
|
||||
}
|
||||
})
|
||||
|
||||
// At this point something goes wrong and we have to restart the machines.
|
||||
for _, node := range tr.nodes {
|
||||
_ = node.Machine.db.Close()
|
||||
}
|
||||
|
||||
participants = make([]string, nodesCount)
|
||||
for i := 0; i < nodesCount; i++ {
|
||||
participants[i] = fmt.Sprintf("Participant#%d", i)
|
||||
}
|
||||
|
||||
newTr := &Transport{}
|
||||
for i := 0; i < nodesCount; i++ {
|
||||
am, err := NewAirgappedMachine(fmt.Sprintf("%s/%s-%d", testDir, testDB, i))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create airgapped machine: %v", err)
|
||||
}
|
||||
am.SetEncryptionKey([]byte(fmt.Sprintf(testDB+"%d", i)))
|
||||
if err = am.InitKeys(); err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
node := Node{
|
||||
ParticipantID: i,
|
||||
Participant: participants[i],
|
||||
Machine: am,
|
||||
deals: tr.nodes[i].deals,
|
||||
}
|
||||
newTr.nodes = append(newTr.nodes, &node)
|
||||
}
|
||||
defer os.RemoveAll(testDir)
|
||||
|
||||
for _, node := range newTr.nodes {
|
||||
err := node.Machine.ReplayOperationsLog(DKGIdentifier)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
//oldTr := tr
|
||||
tr = newTr
|
||||
|
||||
//responses
|
||||
runStep(tr, func(n *Node, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
var payload responses.DKGProposalDealParticipantResponse
|
||||
for _, req := range n.deals {
|
||||
p := responses.DKGProposalDealParticipantEntry{
|
||||
ParticipantId: req.ParticipantId,
|
||||
Addr: fmt.Sprintf("Participant#%d", req.ParticipantId),
|
||||
DkgDeal: req.Deal,
|
||||
}
|
||||
payload = append(payload, &p)
|
||||
}
|
||||
op := createOperation(t, string(dkg_proposal_fsm.StateDkgResponsesAwaitConfirmations), "", payload)
|
||||
|
||||
operation, err := n.Machine.HandleOperation(op)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: failed to handle operation %s: %v", n.Participant, op.Type, err)
|
||||
}
|
||||
for _, msg := range operation.ResultMsgs {
|
||||
tr.BroadcastMessage(t, msg)
|
||||
}
|
||||
})
|
||||
|
||||
//master key
|
||||
runStep(tr, func(n *Node, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
var payload responses.DKGProposalResponseParticipantResponse
|
||||
for _, req := range n.responses {
|
||||
p := responses.DKGProposalResponseParticipantEntry{
|
||||
ParticipantId: req.ParticipantId,
|
||||
Addr: fmt.Sprintf("Participant#%d", req.ParticipantId),
|
||||
DkgResponse: req.Response,
|
||||
}
|
||||
payload = append(payload, &p)
|
||||
}
|
||||
op := createOperation(t, string(dkg_proposal_fsm.StateDkgMasterKeyAwaitConfirmations), "", payload)
|
||||
|
||||
operation, err := n.Machine.HandleOperation(op)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: failed to handle operation %s: %v", n.Participant, op.Type, err)
|
||||
}
|
||||
for _, msg := range operation.ResultMsgs {
|
||||
tr.BroadcastMessage(t, msg)
|
||||
}
|
||||
})
|
||||
|
||||
// check that all master keys are equal
|
||||
for _, n := range tr.nodes {
|
||||
for i := 0; i < len(n.masterKeys); i++ {
|
||||
if !bytes.Equal(n.masterKeys[0].MasterKey, n.masterKeys[i].MasterKey) {
|
||||
t.Fatalf("master keys is not equal!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
msgToSign := []byte("i am a message")
|
||||
|
||||
//partialSigns
|
||||
runStep(tr, func(n *Node, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
payload := responses.SigningPartialSignsParticipantInvitationsResponse{
|
||||
SrcPayload: msgToSign,
|
||||
}
|
||||
|
||||
op := createOperation(t, string(signing_proposal_fsm.StateSigningAwaitPartialSigns), "", payload)
|
||||
|
||||
operation, err := n.Machine.HandleOperation(op)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: failed to handle operation %s: %v", n.Participant, op.Type, err)
|
||||
}
|
||||
for _, msg := range operation.ResultMsgs {
|
||||
tr.BroadcastMessage(t, msg)
|
||||
}
|
||||
})
|
||||
|
||||
//recover full signature
|
||||
runStep(tr, func(n *Node, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
var payload responses.SigningProcessParticipantResponse
|
||||
for _, req := range n.partialSigns {
|
||||
p := responses.SigningProcessParticipantEntry{
|
||||
ParticipantId: req.ParticipantId,
|
||||
Addr: fmt.Sprintf("Participant#%d", req.ParticipantId),
|
||||
PartialSign: req.PartialSign,
|
||||
}
|
||||
payload.Participants = append(payload.Participants, &p)
|
||||
}
|
||||
payload.SrcPayload = msgToSign
|
||||
op := createOperation(t, string(signing_proposal_fsm.StateSigningPartialSignsCollected), "", payload)
|
||||
|
||||
operation, err := n.Machine.HandleOperation(op)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: failed to handle operation %s: %v", n.Participant, op.Type, err)
|
||||
}
|
||||
for _, msg := range operation.ResultMsgs {
|
||||
tr.BroadcastMessage(t, msg)
|
||||
}
|
||||
})
|
||||
|
||||
fmt.Println("DKG succeeded, signature recovered")
|
||||
}
|
||||
|
||||
func runStep(transport *Transport, cb func(n *Node, wg *sync.WaitGroup)) {
|
||||
var wg = &sync.WaitGroup{}
|
||||
for _, node := range transport.nodes {
|
||||
wg.Add(1)
|
||||
n := node
|
||||
go cb(n, wg)
|
||||
cb(n, wg)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@ func (am *AirgappedMachine) createPartialSign(msg []byte, dkgIdentifier string)
|
|||
return nil, fmt.Errorf("failed to load blsKeyring: %w", err)
|
||||
}
|
||||
|
||||
return tbls.Sign(am.suite.(pairing.Suite), blsKeyring.Share, msg)
|
||||
return tbls.Sign(am.baseSuite.(pairing.Suite), blsKeyring.Share, msg)
|
||||
}
|
||||
|
||||
// recoverFullSign recovers full threshold signature for a message
|
||||
|
@ -129,7 +129,7 @@ func (am *AirgappedMachine) recoverFullSign(msg []byte, sigShares [][]byte, t, n
|
|||
return nil, fmt.Errorf("failed to load blsKeyring: %w", err)
|
||||
}
|
||||
|
||||
return tbls.Recover(am.suite.(pairing.Suite), blsKeyring.PubPoly, msg, sigShares, t, n)
|
||||
return tbls.Recover(am.baseSuite.(pairing.Suite), blsKeyring.PubPoly, msg, sigShares, t, n)
|
||||
}
|
||||
|
||||
// verifySign verifies a signature of a message
|
||||
|
@ -139,5 +139,5 @@ func (am *AirgappedMachine) verifySign(msg []byte, fullSignature []byte, dkgIden
|
|||
return fmt.Errorf("failed to load blsKeyring: %w", err)
|
||||
}
|
||||
|
||||
return bls.Verify(am.suite.(pairing.Suite), blsKeyring.PubPoly.Commit(), msg, fullSignature)
|
||||
return bls.Verify(am.baseSuite.(pairing.Suite), blsKeyring.PubPoly.Commit(), msg, fullSignature)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package airgapped
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
bls "github.com/depools/kyber-bls12381"
|
||||
|
||||
"github.com/corestario/kyber"
|
||||
dkgPedersen "github.com/corestario/kyber/share/dkg/pedersen"
|
||||
client "github.com/depools/dc4bc/client/types"
|
||||
|
@ -13,7 +16,6 @@ import (
|
|||
"github.com/depools/dc4bc/fsm/types/requests"
|
||||
"github.com/depools/dc4bc/fsm/types/responses"
|
||||
"github.com/depools/dc4bc/storage"
|
||||
bls12381 "github.com/depools/kyber-bls12381"
|
||||
)
|
||||
|
||||
func createMessage(o client.Operation, data []byte) storage.Message {
|
||||
|
@ -43,7 +45,7 @@ func (am *AirgappedMachine) handleStateAwaitParticipantsConfirmations(o *client.
|
|||
|
||||
pid := -1
|
||||
for _, r := range payload {
|
||||
pubkey := am.suite.Point()
|
||||
pubkey := am.baseSuite.Point()
|
||||
if err := pubkey.UnmarshalBinary(r.DkgPubKey); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal dkg pubkey: %w", err)
|
||||
}
|
||||
|
@ -60,11 +62,17 @@ func (am *AirgappedMachine) handleStateAwaitParticipantsConfirmations(o *client.
|
|||
return fmt.Errorf("dkg instance %s already exists", o.DKGIdentifier)
|
||||
}
|
||||
|
||||
dkgInstance := dkg.Init(am.suite, am.pubKey, am.secKey)
|
||||
// Here we create a new seeded suite for the new DKG round with seed =
|
||||
// sha256.Sum256(baseSeed + DKGIdentifier). We need this to avoid identical
|
||||
// DKG rounds.
|
||||
var (
|
||||
dkgSeed = sha256.Sum256(append([]byte(o.DKGIdentifier), am.baseSeed...))
|
||||
suite = bls.NewBLS12381Suite(dkgSeed[:])
|
||||
)
|
||||
dkgInstance := dkg.Init(suite, am.pubKey, am.secKey)
|
||||
dkgInstance.Threshold = payload[0].Threshold //same for everyone
|
||||
dkgInstance.N = len(payload)
|
||||
am.dkgInstances[o.DKGIdentifier] = dkgInstance
|
||||
|
||||
req := requests.SignatureProposalParticipantRequest{
|
||||
ParticipantId: pid,
|
||||
CreatedAt: o.CreatedAt,
|
||||
|
@ -101,14 +109,14 @@ func (am *AirgappedMachine) handleStateDkgCommitsAwaitConfirmations(o *client.Op
|
|||
}
|
||||
|
||||
for _, entry := range payload {
|
||||
pubKey := bls12381.NewBLS12381Suite().Point()
|
||||
pubKey := am.baseSuite.Point()
|
||||
if err = pubKey.UnmarshalBinary(entry.DkgPubKey); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal pubkey: %w", err)
|
||||
}
|
||||
dkgInstance.StorePubKey(entry.Addr, entry.ParticipantId, pubKey)
|
||||
}
|
||||
|
||||
if err = dkgInstance.InitDKGInstance(); err != nil {
|
||||
if err = dkgInstance.InitDKGInstance(am.baseSeed); err != nil {
|
||||
return fmt.Errorf("failed to init dkg instance: %w", err)
|
||||
}
|
||||
|
||||
|
@ -165,7 +173,7 @@ func (am *AirgappedMachine) handleStateDkgDealsAwaitConfirmations(o *client.Oper
|
|||
}
|
||||
dkgCommits := make([]kyber.Point, 0, len(commitsBz))
|
||||
for _, commitBz := range commitsBz {
|
||||
commit := am.suite.Point()
|
||||
commit := am.baseSuite.Point()
|
||||
if err = commit.UnmarshalBinary(commitBz); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal commit: %w", err)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,242 @@
|
|||
package airgapped
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
bls12381 "github.com/depools/kyber-bls12381"
|
||||
|
||||
client "github.com/depools/dc4bc/client/types"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
)
|
||||
|
||||
const (
|
||||
pubKeyDBKey = "public_key"
|
||||
privateKeyDBKey = "private_key"
|
||||
saltDBKey = "salt_key"
|
||||
baseSeedKey = "base_seed_key"
|
||||
operationsLogDBKey = "operations_log"
|
||||
)
|
||||
|
||||
type RoundOperationLog map[string][]client.Operation
|
||||
|
||||
func (am *AirgappedMachine) loadBaseSeed() error {
|
||||
seed, err := am.getBaseSeed()
|
||||
if errors.Is(err, leveldb.ErrNotFound) {
|
||||
log.Println("Base seed not initialized, generating a new one...")
|
||||
seed = make([]byte, seedSize)
|
||||
_, err = rand.Read(seed)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to rand.Read: %w", err)
|
||||
}
|
||||
|
||||
if err := am.storeBaseSeed(seed); err != nil {
|
||||
return fmt.Errorf("failed to storeBaseSeed: %w", err)
|
||||
}
|
||||
|
||||
log.Println("Successfully generated a new seed")
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("failed to getBaseSeed: %w", err)
|
||||
}
|
||||
|
||||
am.baseSeed = seed
|
||||
am.baseSuite = bls12381.NewBLS12381Suite(am.baseSeed)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (am *AirgappedMachine) storeBaseSeed(seed []byte) error {
|
||||
if err := am.db.Put([]byte(baseSeedKey), seed, nil); err != nil {
|
||||
return fmt.Errorf("failed to put baseSeed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (am *AirgappedMachine) getBaseSeed() ([]byte, error) {
|
||||
seed, err := am.db.Get([]byte(baseSeedKey), nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get baseSeed: %w", err)
|
||||
}
|
||||
|
||||
return seed, nil
|
||||
}
|
||||
|
||||
func (am *AirgappedMachine) storeOperation(o client.Operation) error {
|
||||
roundOperationsLog, err := am.getRoundOperationLog()
|
||||
if err != nil {
|
||||
if err == leveldb.ErrNotFound {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("failed to get operationsLogBz from db: %w", err)
|
||||
}
|
||||
|
||||
operationsLog := roundOperationsLog[o.DKGIdentifier]
|
||||
operationsLog = append(operationsLog, o)
|
||||
roundOperationsLog[o.DKGIdentifier] = operationsLog
|
||||
|
||||
roundOperationsLogBz, err := json.Marshal(roundOperationsLog)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal operationsLog: %w", err)
|
||||
}
|
||||
|
||||
if err := am.db.Put([]byte(operationsLogDBKey), roundOperationsLogBz, nil); err != nil {
|
||||
return fmt.Errorf("failed to put updated operationsLog: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (am *AirgappedMachine) getOperationsLog(dkgIdentifier string) ([]client.Operation, error) {
|
||||
roundOperationsLog, err := am.getRoundOperationLog()
|
||||
if err != nil {
|
||||
if err == leveldb.ErrNotFound {
|
||||
return nil, err
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get operationsLogBz from db: %w", err)
|
||||
}
|
||||
|
||||
operationsLog, ok := roundOperationsLog[dkgIdentifier]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("operation log not found for %s", dkgIdentifier)
|
||||
}
|
||||
|
||||
return operationsLog, nil
|
||||
}
|
||||
|
||||
func (am *AirgappedMachine) dropRoundOperationLog(dkgIdentifier string) error {
|
||||
roundOperationsLog, err := am.getRoundOperationLog()
|
||||
if err != nil {
|
||||
if err == leveldb.ErrNotFound {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("failed to get operationsLogBz from db: %w", err)
|
||||
}
|
||||
|
||||
roundOperationsLog[dkgIdentifier] = []client.Operation{}
|
||||
roundOperationsLogBz, err := json.Marshal(roundOperationsLog)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal operationsLog: %w", err)
|
||||
}
|
||||
|
||||
if err := am.db.Put([]byte(operationsLogDBKey), roundOperationsLogBz, nil); err != nil {
|
||||
return fmt.Errorf("failed to put updated operationsLog: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (am *AirgappedMachine) getRoundOperationLog() (RoundOperationLog, error) {
|
||||
operationsLogBz, err := am.db.Get([]byte(operationsLogDBKey), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var roundOperationsLog RoundOperationLog
|
||||
if err := json.Unmarshal(operationsLogBz, &roundOperationsLog); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal stored operationsLog: %w", err)
|
||||
}
|
||||
|
||||
return roundOperationsLog, nil
|
||||
}
|
||||
|
||||
// LoadKeysFromDB load DKG keys from LevelDB
|
||||
func (am *AirgappedMachine) LoadKeysFromDB() error {
|
||||
pubKeyBz, err := am.db.Get([]byte(pubKeyDBKey), nil)
|
||||
if err != nil {
|
||||
if err == leveldb.ErrNotFound {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("failed to get public key from db: %w", err)
|
||||
}
|
||||
|
||||
privateKeyBz, err := am.db.Get([]byte(privateKeyDBKey), nil)
|
||||
if err != nil {
|
||||
if err == leveldb.ErrNotFound {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("failed to get private key from db: %w", err)
|
||||
}
|
||||
|
||||
salt, err := am.db.Get([]byte(saltDBKey), nil)
|
||||
if err != nil {
|
||||
if err == leveldb.ErrNotFound {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("failed to read salt from db: %w", err)
|
||||
}
|
||||
|
||||
decryptedPubKey, err := decrypt(am.encryptionKey, salt, pubKeyBz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
decryptedPrivateKey, err := decrypt(am.encryptionKey, salt, privateKeyBz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
am.pubKey = am.baseSuite.Point()
|
||||
if err = am.pubKey.UnmarshalBinary(decryptedPubKey); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal public key: %w", err)
|
||||
}
|
||||
|
||||
am.secKey = am.baseSuite.Scalar()
|
||||
if err = am.secKey.UnmarshalBinary(decryptedPrivateKey); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal private key: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SaveKeysToDB save DKG keys to LevelDB
|
||||
func (am *AirgappedMachine) SaveKeysToDB() error {
|
||||
pubKeyBz, err := am.pubKey.MarshalBinary()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal pub key: %w", err)
|
||||
}
|
||||
privateKeyBz, err := am.secKey.MarshalBinary()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal private key: %w", err)
|
||||
}
|
||||
|
||||
salt := make([]byte, 32)
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
return fmt.Errorf("failed to generate salt: %w", err)
|
||||
}
|
||||
|
||||
encryptedPubKey, err := encrypt(am.encryptionKey, salt, pubKeyBz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encryptedPrivateKey, err := encrypt(am.encryptionKey, salt, privateKeyBz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx, err := am.db.OpenTransaction()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open transcation for db: %w", err)
|
||||
}
|
||||
defer tx.Discard()
|
||||
|
||||
if err = tx.Put([]byte(pubKeyDBKey), encryptedPubKey, nil); err != nil {
|
||||
return fmt.Errorf("failed to put pub key into db: %w", err)
|
||||
}
|
||||
|
||||
if err = tx.Put([]byte(privateKeyDBKey), encryptedPrivateKey, nil); err != nil {
|
||||
return fmt.Errorf("failed to put private key into db: %w", err)
|
||||
}
|
||||
|
||||
if err = tx.Put([]byte(saltDBKey), salt, nil); err != nil {
|
||||
return fmt.Errorf("failed to put salt into db: %w", err)
|
||||
}
|
||||
|
||||
if err = tx.Commit(); err != nil {
|
||||
return fmt.Errorf("failed to commit tx for saving keys into db: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -2,9 +2,10 @@ package airgapped
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/depools/dc4bc/dkg"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -16,7 +17,7 @@ func makeBLSKeyKeyringDBKey(key string) string {
|
|||
}
|
||||
|
||||
func (am *AirgappedMachine) saveBLSKeyring(dkgID string, blsKeyring *dkg.BLSKeyring) error {
|
||||
salt, err := am.db.Get([]byte(saltKey), nil)
|
||||
salt, err := am.db.Get([]byte(saltDBKey), nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read salt from db: %w", err)
|
||||
}
|
||||
|
@ -43,7 +44,7 @@ func (am *AirgappedMachine) loadBLSKeyring(dkgID string) (*dkg.BLSKeyring, error
|
|||
err error
|
||||
)
|
||||
|
||||
salt, err := am.db.Get([]byte(saltKey), nil)
|
||||
salt, err := am.db.Get([]byte(saltDBKey), nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read salt from db: %w", err)
|
||||
}
|
||||
|
@ -57,7 +58,7 @@ func (am *AirgappedMachine) loadBLSKeyring(dkgID string) (*dkg.BLSKeyring, error
|
|||
return nil, fmt.Errorf("failed to decrypt BLS keyring: %w", err)
|
||||
}
|
||||
|
||||
if blsKeyring, err = dkg.LoadBLSKeyringFromBytes(decryptedKeyring); err != nil {
|
||||
if blsKeyring, err = dkg.LoadBLSKeyringFromBytes(am.baseSuite, decryptedKeyring); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode bls keyring")
|
||||
}
|
||||
return blsKeyring, nil
|
||||
|
@ -69,7 +70,7 @@ func (am *AirgappedMachine) GetBLSKeyrings() (map[string]*dkg.BLSKeyring, error)
|
|||
err error
|
||||
)
|
||||
|
||||
salt, err := am.db.Get([]byte(saltKey), nil)
|
||||
salt, err := am.db.Get([]byte(saltDBKey), nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read salt from db: %w", err)
|
||||
}
|
||||
|
@ -85,7 +86,7 @@ func (am *AirgappedMachine) GetBLSKeyrings() (map[string]*dkg.BLSKeyring, error)
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decrypt BLS keyring: %w", err)
|
||||
}
|
||||
if blsKeyring, err = dkg.LoadBLSKeyringFromBytes(decryptedKeyring); err != nil {
|
||||
if blsKeyring, err = dkg.LoadBLSKeyringFromBytes(am.baseSuite, decryptedKeyring); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode bls keyring: %w", err)
|
||||
}
|
||||
keyrings[strings.TrimLeft(string(key), blsKeyringPrefix)] = blsKeyring
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
@ -110,7 +111,9 @@ func (n *node) run(t *testing.T) {
|
|||
if err = json.Unmarshal(msg.Data, &pubKeyReq); err != nil {
|
||||
t.Fatalf("failed to unmarshal pubKey request: %v", err)
|
||||
}
|
||||
pubKey := bls12381.NewBLS12381Suite().Point()
|
||||
seed := make([]byte, 32)
|
||||
_, _ = rand.Read(seed)
|
||||
pubKey := bls12381.NewBLS12381Suite(seed).Point()
|
||||
if err = pubKey.UnmarshalBinary(pubKeyReq.MasterKey); err != nil {
|
||||
t.Fatalf("failed to unmarshal pubkey: %v", err)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
@ -52,6 +53,21 @@ func NewTerminal(machine *airgapped.AirgappedMachine) *terminal {
|
|||
commandHandler: t.showFinishedDKGCommand,
|
||||
description: "shows a list of finished dkg rounds",
|
||||
})
|
||||
t.addCommand("replay_operations_log", &terminalCommand{
|
||||
commandHandler: t.replayOperationLogCommand,
|
||||
description: "replays the operation log for a given dkg round",
|
||||
})
|
||||
t.addCommand("drop_operations_log", &terminalCommand{
|
||||
commandHandler: t.dropOperationLogCommand,
|
||||
description: "drops the operation log for a given dkg round",
|
||||
})
|
||||
t.addCommand("exit", &terminalCommand{
|
||||
commandHandler: func() error {
|
||||
log.Fatal("interrupted")
|
||||
return nil
|
||||
},
|
||||
description: "stops the machine",
|
||||
})
|
||||
return &t
|
||||
}
|
||||
|
||||
|
@ -104,6 +120,32 @@ func (t *terminal) showFinishedDKGCommand() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (t *terminal) replayOperationLogCommand() error {
|
||||
fmt.Print("> Enter the DKGRoundIdentifier: ")
|
||||
dkgRoundIdentifier, err := t.reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read dkgRoundIdentifier: %w", err)
|
||||
}
|
||||
|
||||
if err := t.airgapped.ReplayOperationsLog(dkgRoundIdentifier); err != nil {
|
||||
return fmt.Errorf("failed to ReplayOperationsLog: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *terminal) dropOperationLogCommand() error {
|
||||
fmt.Print("> Enter the DKGRoundIdentifier: ")
|
||||
dkgRoundIdentifier, err := t.reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read dkgRoundIdentifier: %w", err)
|
||||
}
|
||||
|
||||
if err := t.airgapped.DropOperationsLog(dkgRoundIdentifier); err != nil {
|
||||
return fmt.Errorf("failed to DropOperationsLog: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *terminal) enterEncryptionPasswordIfNeeded() error {
|
||||
t.airgapped.Lock()
|
||||
defer t.airgapped.Unlock()
|
||||
|
@ -153,7 +195,7 @@ func (t *terminal) run() error {
|
|||
}
|
||||
t.airgapped.Lock()
|
||||
if err := handler.commandHandler(); err != nil {
|
||||
fmt.Printf("failled to execute command %s: %v, \n", command, err)
|
||||
fmt.Printf("failled to execute command %s: %v \n", command, err)
|
||||
t.airgapped.Unlock()
|
||||
continue
|
||||
}
|
||||
|
@ -194,6 +236,14 @@ func main() {
|
|||
log.Fatalf("failed to init airgapped machine %v", err)
|
||||
}
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
for _ = range c {
|
||||
fmt.Printf("Intercepting SIGINT, please type `exit` to stop the machine\n>>> ")
|
||||
}
|
||||
}()
|
||||
|
||||
t := NewTerminal(air)
|
||||
go t.dropSensitiveData(passwordLifeDuration)
|
||||
if err = t.run(); err != nil {
|
||||
|
|
31
dkg/dkg.go
31
dkg/dkg.go
|
@ -7,11 +7,12 @@ import (
|
|||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/corestario/kyber/share"
|
||||
|
||||
"github.com/corestario/kyber"
|
||||
"github.com/corestario/kyber/share"
|
||||
dkg "github.com/corestario/kyber/share/dkg/pedersen"
|
||||
vss "github.com/corestario/kyber/share/vss/pedersen"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"lukechampine.com/frand"
|
||||
)
|
||||
|
||||
// TODO: dump necessary data on disk
|
||||
|
@ -46,6 +47,26 @@ func Init(suite vss.Suite, pubKey kyber.Point, secKey kyber.Scalar) *DKG {
|
|||
return &d
|
||||
}
|
||||
|
||||
func (d *DKG) Equals(other *DKG) error {
|
||||
for addr, commits := range d.commits {
|
||||
otherCommits := other.commits[addr]
|
||||
for idx := range commits {
|
||||
if !commits[idx].Equal(otherCommits[idx]) {
|
||||
return fmt.Errorf("commits from %s are not equal (idx %d): %v != %v", addr, idx, commits[idx], otherCommits[idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for addr, deal := range d.deals {
|
||||
otherDeal := other.deals[addr]
|
||||
if !cmp.Equal(deal.Deal, otherDeal.Deal) {
|
||||
return fmt.Errorf("deals from %s are not equal: %+v != %+v", addr, deal.Deal, otherDeal.Deal)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DKG) GetPubKey() kyber.Point {
|
||||
return d.pubKey
|
||||
}
|
||||
|
@ -90,7 +111,7 @@ func (d *DKG) calcParticipantID() int {
|
|||
return -1
|
||||
}
|
||||
|
||||
func (d *DKG) InitDKGInstance() (err error) {
|
||||
func (d *DKG) InitDKGInstance(seed []byte) (err error) {
|
||||
sort.Sort(d.pubkeys)
|
||||
|
||||
publicKeys := d.pubkeys.GetPKs()
|
||||
|
@ -107,7 +128,9 @@ func (d *DKG) InitDKGInstance() (err error) {
|
|||
|
||||
d.responses = newMessageStore(int(math.Pow(float64(participantsCount)-1, 2)))
|
||||
|
||||
d.instance, err = dkg.NewDistKeyGenerator(d.suite, d.secKey, publicKeys, d.Threshold)
|
||||
reader := frand.NewCustom(seed, 32, 20)
|
||||
|
||||
d.instance, err = dkg.NewDistKeyGenerator(d.suite, d.secKey, publicKeys, d.Threshold, reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
10
dkg/types.go
10
dkg/types.go
|
@ -7,10 +7,10 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/corestario/kyber/pairing"
|
||||
vss "github.com/corestario/kyber/share/vss/pedersen"
|
||||
|
||||
"github.com/corestario/kyber"
|
||||
"github.com/corestario/kyber/share"
|
||||
bls12381 "github.com/depools/kyber-bls12381"
|
||||
)
|
||||
|
||||
type PK2Participant struct {
|
||||
|
@ -147,7 +147,7 @@ func (b *BLSKeyring) Bytes() ([]byte, error) {
|
|||
}
|
||||
|
||||
// LoadBLSKeyringFromBytes decode the form generated by Bytes()
|
||||
func LoadBLSKeyringFromBytes(data []byte) (*BLSKeyring, error) {
|
||||
func LoadBLSKeyringFromBytes(suite vss.Suite, data []byte) (*BLSKeyring, error) {
|
||||
var (
|
||||
err error
|
||||
blsKeyringJson blsKeyringJSON
|
||||
|
@ -158,20 +158,20 @@ func LoadBLSKeyringFromBytes(data []byte) (*BLSKeyring, error) {
|
|||
|
||||
commitments := make([]kyber.Point, 0, len(blsKeyringJson.Commitments))
|
||||
for _, commitmentBz := range blsKeyringJson.Commitments {
|
||||
commitment := bls12381.NewBLS12381Suite().Point()
|
||||
commitment := suite.Point()
|
||||
if err := commitment.UnmarshalBinary(commitmentBz); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal commitment: %w", err)
|
||||
}
|
||||
commitments = append(commitments, commitment)
|
||||
}
|
||||
|
||||
priShare, privDec := &share.PriShare{V: bls12381.NewBLS12381Suite().(pairing.Suite).G1().Scalar()}, gob.NewDecoder(bytes.NewBuffer(blsKeyringJson.Share))
|
||||
priShare, privDec := &share.PriShare{V: suite.(pairing.Suite).G1().Scalar()}, gob.NewDecoder(bytes.NewBuffer(blsKeyringJson.Share))
|
||||
if err := privDec.Decode(priShare); err != nil {
|
||||
return nil, fmt.Errorf("failed to share: %v", err)
|
||||
}
|
||||
|
||||
return &BLSKeyring{
|
||||
PubPoly: share.NewPubPoly(bls12381.NewBLS12381Suite(), nil, commitments),
|
||||
PubPoly: share.NewPubPoly(suite, nil, commitments),
|
||||
Share: priShare,
|
||||
}, nil
|
||||
}
|
||||
|
|
6
go.mod
6
go.mod
|
@ -6,6 +6,7 @@ require (
|
|||
github.com/corestario/kyber v1.3.0
|
||||
github.com/depools/kyber-bls12381 v0.0.0-20200831104422-978ac58f592e
|
||||
github.com/golang/mock v1.4.4
|
||||
github.com/google/go-cmp v0.2.0
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b
|
||||
github.com/looplab/fsm v0.1.0
|
||||
|
@ -19,6 +20,11 @@ require (
|
|||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
|
||||
golang.org/x/text v0.3.3 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
|
||||
lukechampine.com/frand v1.3.0
|
||||
)
|
||||
|
||||
replace golang.org/x/crypto => github.com/tendermint/crypto v0.0.0-20180820045704-3764759f34a5
|
||||
|
||||
replace github.com/corestario/kyber => ../kyber
|
||||
|
||||
replace github.com/depools/kyber-bls12381 => ../kyber-bls12381
|
||||
|
|
9
go.sum
9
go.sum
|
@ -1,6 +1,8 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
|
@ -13,8 +15,6 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc
|
|||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/corestario/kyber v1.3.0 h1:SEWofdorUUeAJTsa9WJmrUYFyWHSWyXLgqDTFFEIzes=
|
||||
github.com/corestario/kyber v1.3.0/go.mod h1:kIWfWekm8kSJNti3Fo3DCV0GHEH050MWQrdvZdefbkk=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
|
@ -47,7 +47,9 @@ github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8l
|
|||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
|
@ -180,6 +182,7 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191025090151-53bf42e6b339/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y=
|
||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -216,3 +219,5 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
lukechampine.com/frand v1.3.0 h1:HFLrwEHr78+EqAfyp8OChgEzdYCVZzzj6Y+cGDQRhaI=
|
||||
lukechampine.com/frand v1.3.0/go.mod h1:4S/TM2ZgrKejMcKMbeLjISpJMO+/eZ1zu3vYX9dtj3s=
|
||||
|
|
Loading…
Reference in New Issue