mirror of https://github.com/certusone/dc4bc.git
Merge branch 'master' into feat/qr-chunks-as-gif
This commit is contained in:
commit
3c7dc184a4
12
Makefile
12
Makefile
|
@ -12,18 +12,18 @@ mocks:
|
||||||
|
|
||||||
build-darwin:
|
build-darwin:
|
||||||
@echo "Building dc4bc_d..."
|
@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..."
|
@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..."
|
@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:
|
build-linux:
|
||||||
@echo "Building dc4bc_d..."
|
@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..."
|
@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..."
|
@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
|
.PHONY: mocks
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package airgapped
|
package airgapped
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
@ -20,14 +19,11 @@ import (
|
||||||
"github.com/depools/dc4bc/fsm/state_machines/signing_proposal_fsm"
|
"github.com/depools/dc4bc/fsm/state_machines/signing_proposal_fsm"
|
||||||
"github.com/depools/dc4bc/fsm/types/requests"
|
"github.com/depools/dc4bc/fsm/types/requests"
|
||||||
"github.com/depools/dc4bc/qr"
|
"github.com/depools/dc4bc/qr"
|
||||||
bls12381 "github.com/depools/kyber-bls12381"
|
|
||||||
"github.com/syndtr/goleveldb/leveldb"
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
pubKeyDBKey = "public_key"
|
seedSize = 32
|
||||||
privateKeyDBKey = "private_key"
|
|
||||||
saltKey = "salt_key"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type AirgappedMachine struct {
|
type AirgappedMachine struct {
|
||||||
|
@ -39,7 +35,8 @@ type AirgappedMachine struct {
|
||||||
encryptionKey []byte
|
encryptionKey []byte
|
||||||
pubKey kyber.Point
|
pubKey kyber.Point
|
||||||
secKey kyber.Scalar
|
secKey kyber.Scalar
|
||||||
suite vss.Suite
|
baseSuite vss.Suite
|
||||||
|
baseSeed []byte
|
||||||
|
|
||||||
db *leveldb.DB
|
db *leveldb.DB
|
||||||
resultQRFolder string
|
resultQRFolder string
|
||||||
|
@ -55,12 +52,25 @@ func NewAirgappedMachine(dbPath string) (*AirgappedMachine, error) {
|
||||||
qrProcessor: qr.NewCameraProcessor(),
|
qrProcessor: qr.NewCameraProcessor(),
|
||||||
}
|
}
|
||||||
|
|
||||||
am.suite = bls12381.NewBLS12381Suite()
|
|
||||||
|
|
||||||
if am.db, err = leveldb.OpenFile(dbPath, nil); err != nil {
|
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)
|
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
|
return am, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,8 +94,8 @@ func (am *AirgappedMachine) InitKeys() error {
|
||||||
}
|
}
|
||||||
// if keys were not generated yet
|
// if keys were not generated yet
|
||||||
if err == leveldb.ErrNotFound {
|
if err == leveldb.ErrNotFound {
|
||||||
am.secKey = am.suite.Scalar().Pick(am.suite.RandomStream())
|
am.secKey = am.baseSuite.Scalar().Pick(am.baseSuite.RandomStream())
|
||||||
am.pubKey = am.suite.Point().Mul(am.secKey, nil)
|
am.pubKey = am.baseSuite.Point().Mul(am.secKey, nil)
|
||||||
return am.SaveKeysToDB()
|
return am.SaveKeysToDB()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,100 +123,27 @@ func (am *AirgappedMachine) DropSensitiveData() {
|
||||||
am.encryptionKey = nil
|
am.encryptionKey = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadKeysFromDB load DKG keys from LevelDB
|
func (am *AirgappedMachine) ReplayOperationsLog(dkgIdentifier string) error {
|
||||||
func (am *AirgappedMachine) LoadKeysFromDB() error {
|
operationsLog, err := am.getOperationsLog(dkgIdentifier)
|
||||||
pubKeyBz, err := am.db.Get([]byte(pubKeyDBKey), nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == leveldb.ErrNotFound {
|
return fmt.Errorf("failed to getOperationsLog: %w", err)
|
||||||
return err
|
|
||||||
}
|
|
||||||
return fmt.Errorf("failed to get public key from db: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
privateKeyBz, err := am.db.Get([]byte(privateKeyDBKey), nil)
|
for _, operation := range operationsLog {
|
||||||
if err != nil {
|
if _, err := am.HandleOperation(operation); err != nil {
|
||||||
if err == leveldb.ErrNotFound {
|
return fmt.Errorf(
|
||||||
return err
|
"failed to HandleOperation %s (this error is fatal, the state can not be recovered): %w",
|
||||||
|
operation.ID, err)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("failed to get private key from db: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
salt, err := am.db.Get([]byte(saltKey), nil)
|
log.Println("Successfully replayed Operation log")
|
||||||
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveKeysToDB save DKG keys to LevelDB
|
func (am *AirgappedMachine) DropOperationsLog(dkgIdentifier string) error {
|
||||||
func (am *AirgappedMachine) SaveKeysToDB() error {
|
return am.dropRoundOperationLog(dkgIdentifier)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getParticipantID returns our own participant id for the given DKG round
|
// getParticipantID returns our own participant id for the given DKG round
|
||||||
|
@ -230,7 +167,7 @@ func (am *AirgappedMachine) encryptDataForParticipant(dkgIdentifier, to string,
|
||||||
return nil, fmt.Errorf("failed to get pk for participant %s: %w", to, err)
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to encrypt data: %w", err)
|
return nil, fmt.Errorf("failed to encrypt data: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -239,7 +176,7 @@ func (am *AirgappedMachine) encryptDataForParticipant(dkgIdentifier, to string,
|
||||||
|
|
||||||
// decryptDataFromParticipant decrypts the data that was sent to us
|
// decryptDataFromParticipant decrypts the data that was sent to us
|
||||||
func (am *AirgappedMachine) decryptDataFromParticipant(data []byte) ([]byte, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to decrypt data: %w", err)
|
return nil, fmt.Errorf("failed to decrypt data: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -248,6 +185,14 @@ func (am *AirgappedMachine) decryptDataFromParticipant(data []byte) ([]byte, err
|
||||||
|
|
||||||
// HandleOperation handles and processes an operation
|
// HandleOperation handles and processes an operation
|
||||||
func (am *AirgappedMachine) HandleOperation(operation client.Operation) (client.Operation, error) {
|
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 (
|
var (
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,6 +9,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
client "github.com/depools/dc4bc/client/types"
|
client "github.com/depools/dc4bc/client/types"
|
||||||
"github.com/depools/dc4bc/fsm/fsm"
|
"github.com/depools/dc4bc/fsm/fsm"
|
||||||
"github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_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")
|
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)) {
|
func runStep(transport *Transport, cb func(n *Node, wg *sync.WaitGroup)) {
|
||||||
var wg = &sync.WaitGroup{}
|
var wg = &sync.WaitGroup{}
|
||||||
for _, node := range transport.nodes {
|
for _, node := range transport.nodes {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
n := node
|
n := node
|
||||||
go cb(n, wg)
|
cb(n, wg)
|
||||||
}
|
}
|
||||||
wg.Wait()
|
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 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
|
// 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 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
|
// 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 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
|
package airgapped
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
bls "github.com/depools/kyber-bls12381"
|
||||||
|
|
||||||
"github.com/corestario/kyber"
|
"github.com/corestario/kyber"
|
||||||
dkgPedersen "github.com/corestario/kyber/share/dkg/pedersen"
|
dkgPedersen "github.com/corestario/kyber/share/dkg/pedersen"
|
||||||
client "github.com/depools/dc4bc/client/types"
|
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/requests"
|
||||||
"github.com/depools/dc4bc/fsm/types/responses"
|
"github.com/depools/dc4bc/fsm/types/responses"
|
||||||
"github.com/depools/dc4bc/storage"
|
"github.com/depools/dc4bc/storage"
|
||||||
bls12381 "github.com/depools/kyber-bls12381"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func createMessage(o client.Operation, data []byte) storage.Message {
|
func createMessage(o client.Operation, data []byte) storage.Message {
|
||||||
|
@ -43,7 +45,7 @@ func (am *AirgappedMachine) handleStateAwaitParticipantsConfirmations(o *client.
|
||||||
|
|
||||||
pid := -1
|
pid := -1
|
||||||
for _, r := range payload {
|
for _, r := range payload {
|
||||||
pubkey := am.suite.Point()
|
pubkey := am.baseSuite.Point()
|
||||||
if err := pubkey.UnmarshalBinary(r.DkgPubKey); err != nil {
|
if err := pubkey.UnmarshalBinary(r.DkgPubKey); err != nil {
|
||||||
return fmt.Errorf("failed to unmarshal dkg pubkey: %w", err)
|
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)
|
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.Threshold = payload[0].Threshold //same for everyone
|
||||||
dkgInstance.N = len(payload)
|
dkgInstance.N = len(payload)
|
||||||
am.dkgInstances[o.DKGIdentifier] = dkgInstance
|
am.dkgInstances[o.DKGIdentifier] = dkgInstance
|
||||||
|
|
||||||
req := requests.SignatureProposalParticipantRequest{
|
req := requests.SignatureProposalParticipantRequest{
|
||||||
ParticipantId: pid,
|
ParticipantId: pid,
|
||||||
CreatedAt: o.CreatedAt,
|
CreatedAt: o.CreatedAt,
|
||||||
|
@ -101,14 +109,14 @@ func (am *AirgappedMachine) handleStateDkgCommitsAwaitConfirmations(o *client.Op
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, entry := range payload {
|
for _, entry := range payload {
|
||||||
pubKey := bls12381.NewBLS12381Suite().Point()
|
pubKey := am.baseSuite.Point()
|
||||||
if err = pubKey.UnmarshalBinary(entry.DkgPubKey); err != nil {
|
if err = pubKey.UnmarshalBinary(entry.DkgPubKey); err != nil {
|
||||||
return fmt.Errorf("failed to unmarshal pubkey: %w", err)
|
return fmt.Errorf("failed to unmarshal pubkey: %w", err)
|
||||||
}
|
}
|
||||||
dkgInstance.StorePubKey(entry.Addr, entry.ParticipantId, pubKey)
|
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)
|
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))
|
dkgCommits := make([]kyber.Point, 0, len(commitsBz))
|
||||||
for _, commitBz := range commitsBz {
|
for _, commitBz := range commitsBz {
|
||||||
commit := am.suite.Point()
|
commit := am.baseSuite.Point()
|
||||||
if err = commit.UnmarshalBinary(commitBz); err != nil {
|
if err = commit.UnmarshalBinary(commitBz); err != nil {
|
||||||
return fmt.Errorf("failed to unmarshal commit: %w", err)
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/depools/dc4bc/dkg"
|
"github.com/depools/dc4bc/dkg"
|
||||||
"github.com/syndtr/goleveldb/leveldb/util"
|
"github.com/syndtr/goleveldb/leveldb/util"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -16,7 +17,7 @@ func makeBLSKeyKeyringDBKey(key string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am *AirgappedMachine) saveBLSKeyring(dkgID string, blsKeyring *dkg.BLSKeyring) error {
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read salt from db: %w", err)
|
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
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
salt, err := am.db.Get([]byte(saltKey), nil)
|
salt, err := am.db.Get([]byte(saltDBKey), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read salt from db: %w", err)
|
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)
|
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 nil, fmt.Errorf("failed to decode bls keyring")
|
||||||
}
|
}
|
||||||
return blsKeyring, nil
|
return blsKeyring, nil
|
||||||
|
@ -69,7 +70,7 @@ func (am *AirgappedMachine) GetBLSKeyrings() (map[string]*dkg.BLSKeyring, error)
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
salt, err := am.db.Get([]byte(saltKey), nil)
|
salt, err := am.db.Get([]byte(saltDBKey), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read salt from db: %w", err)
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to decrypt BLS keyring: %w", err)
|
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)
|
return nil, fmt.Errorf("failed to decode bls keyring: %w", err)
|
||||||
}
|
}
|
||||||
keyrings[strings.TrimLeft(string(key), blsKeyringPrefix)] = blsKeyring
|
keyrings[strings.TrimLeft(string(key), blsKeyringPrefix)] = blsKeyring
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
|
"crypto/rand"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -110,7 +111,9 @@ func (n *node) run(t *testing.T) {
|
||||||
if err = json.Unmarshal(msg.Data, &pubKeyReq); err != nil {
|
if err = json.Unmarshal(msg.Data, &pubKeyReq); err != nil {
|
||||||
t.Fatalf("failed to unmarshal pubKey request: %v", err)
|
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 {
|
if err = pubKey.UnmarshalBinary(pubKeyReq.MasterKey); err != nil {
|
||||||
t.Fatalf("failed to unmarshal pubkey: %v", err)
|
t.Fatalf("failed to unmarshal pubkey: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
@ -52,6 +53,21 @@ func NewTerminal(machine *airgapped.AirgappedMachine) *terminal {
|
||||||
commandHandler: t.showFinishedDKGCommand,
|
commandHandler: t.showFinishedDKGCommand,
|
||||||
description: "shows a list of finished dkg rounds",
|
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
|
return &t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,6 +118,32 @@ func (t *terminal) showFinishedDKGCommand() error {
|
||||||
return nil
|
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 {
|
func (t *terminal) enterEncryptionPasswordIfNeeded() error {
|
||||||
t.airgapped.Lock()
|
t.airgapped.Lock()
|
||||||
defer t.airgapped.Unlock()
|
defer t.airgapped.Unlock()
|
||||||
|
@ -151,7 +193,7 @@ func (t *terminal) run() error {
|
||||||
}
|
}
|
||||||
t.airgapped.Lock()
|
t.airgapped.Lock()
|
||||||
if err := handler.commandHandler(); err != nil {
|
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()
|
t.airgapped.Unlock()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -201,6 +243,14 @@ func main() {
|
||||||
air.SetQRProcessorChunkSize(chunkSize)
|
air.SetQRProcessorChunkSize(chunkSize)
|
||||||
air.SetResultQRFolder(qrCodesFolder)
|
air.SetResultQRFolder(qrCodesFolder)
|
||||||
|
|
||||||
|
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)
|
t := NewTerminal(air)
|
||||||
go t.dropSensitiveData(passwordLifeDuration)
|
go t.dropSensitiveData(passwordLifeDuration)
|
||||||
if err = t.run(); err != nil {
|
if err = t.run(); err != nil {
|
||||||
|
|
31
dkg/dkg.go
31
dkg/dkg.go
|
@ -7,11 +7,12 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/corestario/kyber/share"
|
|
||||||
|
|
||||||
"github.com/corestario/kyber"
|
"github.com/corestario/kyber"
|
||||||
|
"github.com/corestario/kyber/share"
|
||||||
dkg "github.com/corestario/kyber/share/dkg/pedersen"
|
dkg "github.com/corestario/kyber/share/dkg/pedersen"
|
||||||
vss "github.com/corestario/kyber/share/vss/pedersen"
|
vss "github.com/corestario/kyber/share/vss/pedersen"
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"lukechampine.com/frand"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: dump necessary data on disk
|
// TODO: dump necessary data on disk
|
||||||
|
@ -46,6 +47,26 @@ func Init(suite vss.Suite, pubKey kyber.Point, secKey kyber.Scalar) *DKG {
|
||||||
return &d
|
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 {
|
func (d *DKG) GetPubKey() kyber.Point {
|
||||||
return d.pubKey
|
return d.pubKey
|
||||||
}
|
}
|
||||||
|
@ -90,7 +111,7 @@ func (d *DKG) calcParticipantID() int {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DKG) InitDKGInstance() (err error) {
|
func (d *DKG) InitDKGInstance(seed []byte) (err error) {
|
||||||
sort.Sort(d.pubkeys)
|
sort.Sort(d.pubkeys)
|
||||||
|
|
||||||
publicKeys := d.pubkeys.GetPKs()
|
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.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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
10
dkg/types.go
10
dkg/types.go
|
@ -7,10 +7,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/corestario/kyber/pairing"
|
"github.com/corestario/kyber/pairing"
|
||||||
|
vss "github.com/corestario/kyber/share/vss/pedersen"
|
||||||
|
|
||||||
"github.com/corestario/kyber"
|
"github.com/corestario/kyber"
|
||||||
"github.com/corestario/kyber/share"
|
"github.com/corestario/kyber/share"
|
||||||
bls12381 "github.com/depools/kyber-bls12381"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type PK2Participant struct {
|
type PK2Participant struct {
|
||||||
|
@ -147,7 +147,7 @@ func (b *BLSKeyring) Bytes() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadBLSKeyringFromBytes decode the form generated by Bytes()
|
// LoadBLSKeyringFromBytes decode the form generated by Bytes()
|
||||||
func LoadBLSKeyringFromBytes(data []byte) (*BLSKeyring, error) {
|
func LoadBLSKeyringFromBytes(suite vss.Suite, data []byte) (*BLSKeyring, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
blsKeyringJson blsKeyringJSON
|
blsKeyringJson blsKeyringJSON
|
||||||
|
@ -158,20 +158,20 @@ func LoadBLSKeyringFromBytes(data []byte) (*BLSKeyring, error) {
|
||||||
|
|
||||||
commitments := make([]kyber.Point, 0, len(blsKeyringJson.Commitments))
|
commitments := make([]kyber.Point, 0, len(blsKeyringJson.Commitments))
|
||||||
for _, commitmentBz := range blsKeyringJson.Commitments {
|
for _, commitmentBz := range blsKeyringJson.Commitments {
|
||||||
commitment := bls12381.NewBLS12381Suite().Point()
|
commitment := suite.Point()
|
||||||
if err := commitment.UnmarshalBinary(commitmentBz); err != nil {
|
if err := commitment.UnmarshalBinary(commitmentBz); err != nil {
|
||||||
return nil, fmt.Errorf("failed to unmarshal commitment: %w", err)
|
return nil, fmt.Errorf("failed to unmarshal commitment: %w", err)
|
||||||
}
|
}
|
||||||
commitments = append(commitments, commitment)
|
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 {
|
if err := privDec.Decode(priShare); err != nil {
|
||||||
return nil, fmt.Errorf("failed to share: %v", err)
|
return nil, fmt.Errorf("failed to share: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &BLSKeyring{
|
return &BLSKeyring{
|
||||||
PubPoly: share.NewPubPoly(bls12381.NewBLS12381Suite(), nil, commitments),
|
PubPoly: share.NewPubPoly(suite, nil, commitments),
|
||||||
Share: priShare,
|
Share: priShare,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
8
go.mod
8
go.mod
|
@ -3,9 +3,10 @@ module github.com/depools/dc4bc
|
||||||
go 1.13
|
go 1.13
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/corestario/kyber v1.3.0
|
github.com/corestario/kyber v1.4.0
|
||||||
github.com/depools/kyber-bls12381 v0.0.0-20200831104422-978ac58f592e
|
github.com/depools/kyber-bls12381 v0.0.0-20200929134032-c24859b7d890
|
||||||
github.com/golang/mock v1.4.4
|
github.com/golang/mock v1.4.4
|
||||||
|
github.com/google/go-cmp v0.2.0
|
||||||
github.com/google/uuid v1.1.1
|
github.com/google/uuid v1.1.1
|
||||||
github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b
|
github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b
|
||||||
github.com/looplab/fsm v0.1.0
|
github.com/looplab/fsm v0.1.0
|
||||||
|
@ -19,6 +20,9 @@ require (
|
||||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
|
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
|
||||||
golang.org/x/text v0.3.3 // indirect
|
golang.org/x/text v0.3.3 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // 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 golang.org/x/crypto => github.com/tendermint/crypto v0.0.0-20180820045704-3764759f34a5
|
||||||
|
|
||||||
|
replace github.com/corestario/kyber => ../kyber
|
||||||
|
|
9
go.sum
9
go.sum
|
@ -1,6 +1,8 @@
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
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/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
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/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/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=
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
|
@ -21,6 +23,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/depools/kyber-bls12381 v0.0.0-20200831104422-978ac58f592e h1:pi/Oli1/ryp6wx4vbGM9qEBd6LDqFN5xH+FG72x2H8I=
|
github.com/depools/kyber-bls12381 v0.0.0-20200831104422-978ac58f592e h1:pi/Oli1/ryp6wx4vbGM9qEBd6LDqFN5xH+FG72x2H8I=
|
||||||
github.com/depools/kyber-bls12381 v0.0.0-20200831104422-978ac58f592e/go.mod h1:4sthQ2LjQIoysUBx/d+QbtjXksr3L0mgKf/Wuzy1M/4=
|
github.com/depools/kyber-bls12381 v0.0.0-20200831104422-978ac58f592e/go.mod h1:4sthQ2LjQIoysUBx/d+QbtjXksr3L0mgKf/Wuzy1M/4=
|
||||||
|
github.com/depools/kyber-bls12381 v0.0.0-20200929134032-c24859b7d890 h1:ra3VcXLAwGdHzcPRXkDVVr2Gb9wpi+XHyljk0J566vs=
|
||||||
|
github.com/depools/kyber-bls12381 v0.0.0-20200929134032-c24859b7d890/go.mod h1:82QP3olqMtRnlRCNxEc9/EKk1qlFCOklxasHvSnXMSI=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw=
|
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw=
|
||||||
|
@ -47,7 +51,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 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
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/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.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 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
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=
|
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
|
@ -180,6 +186,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-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-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-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-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 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y=
|
||||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
@ -216,3 +223,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 h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
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=
|
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