2020-08-10 09:00:29 -07:00
|
|
|
package airgapped
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"github.com/depools/dc4bc/client"
|
|
|
|
"github.com/depools/dc4bc/dkg"
|
|
|
|
"github.com/depools/dc4bc/fsm/fsm"
|
|
|
|
"github.com/depools/dc4bc/fsm/state_machines/dkg_proposal_fsm"
|
2020-08-11 06:42:32 -07:00
|
|
|
"github.com/depools/dc4bc/fsm/state_machines/signature_proposal_fsm"
|
2020-08-10 09:00:29 -07:00
|
|
|
"github.com/depools/dc4bc/fsm/types/requests"
|
|
|
|
"github.com/depools/dc4bc/fsm/types/responses"
|
|
|
|
"github.com/depools/dc4bc/qr"
|
2020-08-17 03:22:46 -07:00
|
|
|
"github.com/syndtr/goleveldb/leveldb"
|
2020-08-10 09:00:29 -07:00
|
|
|
"go.dedis.ch/kyber/v3"
|
2020-08-11 06:42:32 -07:00
|
|
|
"go.dedis.ch/kyber/v3/encrypt/ecies"
|
2020-08-10 09:00:29 -07:00
|
|
|
"go.dedis.ch/kyber/v3/pairing/bn256"
|
2020-08-14 07:38:58 -07:00
|
|
|
"go.dedis.ch/kyber/v3/share"
|
2020-08-11 06:42:32 -07:00
|
|
|
dkgPedersen "go.dedis.ch/kyber/v3/share/dkg/pedersen"
|
2020-08-14 07:38:58 -07:00
|
|
|
"go.dedis.ch/kyber/v3/sign/bls"
|
|
|
|
"go.dedis.ch/kyber/v3/sign/tbls"
|
2020-08-14 03:55:01 -07:00
|
|
|
"log"
|
2020-08-11 06:42:32 -07:00
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2020-08-17 03:22:46 -07:00
|
|
|
resultQRFolder = "result_qr_codes"
|
|
|
|
pubKeyDBKey = "public_key"
|
|
|
|
privateKeyDBKey = "private_key"
|
2020-08-10 09:00:29 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
type AirgappedMachine struct {
|
2020-08-11 06:42:32 -07:00
|
|
|
sync.Mutex
|
|
|
|
|
|
|
|
dkgInstances map[string]*dkg.DKG
|
2020-08-11 02:46:13 -07:00
|
|
|
qrProcessor qr.Processor
|
2020-08-13 08:27:28 -07:00
|
|
|
|
|
|
|
pubKey kyber.Point
|
|
|
|
secKey kyber.Scalar
|
|
|
|
suite *bn256.Suite
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
|
|
|
|
2020-08-17 03:22:46 -07:00
|
|
|
func NewAirgappedMachine(dbPath string) (*AirgappedMachine, error) {
|
2020-08-13 08:27:28 -07:00
|
|
|
am := &AirgappedMachine{
|
2020-08-11 02:46:13 -07:00
|
|
|
dkgInstances: make(map[string]*dkg.DKG),
|
|
|
|
qrProcessor: qr.NewCameraProcessor(),
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
2020-08-13 08:27:28 -07:00
|
|
|
|
|
|
|
am.suite = bn256.NewSuiteG2()
|
|
|
|
|
2020-08-17 03:22:46 -07:00
|
|
|
err := am.loadKeysFromDB(dbPath)
|
|
|
|
if err != nil && err != leveldb.ErrNotFound {
|
|
|
|
return nil, fmt.Errorf("failed to load keys from db %s: %w", dbPath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
|
|
|
return am, am.saveKeysToDB(dbPath)
|
|
|
|
}
|
|
|
|
|
|
|
|
return am, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (am *AirgappedMachine) loadKeysFromDB(dbPath string) error {
|
|
|
|
db, err := leveldb.OpenFile(dbPath, nil)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to open db file %s for keys: %w", dbPath, err)
|
|
|
|
}
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
pubKeyBz, err := db.Get([]byte(pubKeyDBKey), nil)
|
|
|
|
if err != nil {
|
|
|
|
if err == leveldb.ErrNotFound {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return fmt.Errorf("failed to get public key from db %s: %w", dbPath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
privateKeyBz, err := db.Get([]byte(privateKeyDBKey), nil)
|
|
|
|
if err != nil {
|
|
|
|
if err == leveldb.ErrNotFound {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return fmt.Errorf("failed to get private key from db %s: %w", dbPath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
am.pubKey = am.suite.Point()
|
|
|
|
if err = am.pubKey.UnmarshalBinary(pubKeyBz); err != nil {
|
|
|
|
return fmt.Errorf("failed to unmarshal public key: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
am.secKey = am.suite.Scalar()
|
|
|
|
if err = am.secKey.UnmarshalBinary(privateKeyBz); err != nil {
|
|
|
|
return fmt.Errorf("failed to unmarshal private key: %w", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (am *AirgappedMachine) saveKeysToDB(dbPath string) error {
|
|
|
|
db, err := leveldb.OpenFile(dbPath, nil)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to open db file %s for keys: %w", dbPath, err)
|
|
|
|
}
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
tx, err := db.OpenTransaction()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to open transcation for db %s: %w", dbPath, err)
|
|
|
|
}
|
|
|
|
defer tx.Discard()
|
|
|
|
|
|
|
|
if err = tx.Put([]byte(pubKeyDBKey), pubKeyBz, nil); err != nil {
|
|
|
|
return fmt.Errorf("failed to put pub key into db %s: %w", dbPath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = tx.Put([]byte(privateKeyDBKey), privateKeyBz, nil); err != nil {
|
|
|
|
return fmt.Errorf("failed to put private key into db %s: %w", dbPath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = tx.Commit(); err != nil {
|
|
|
|
return fmt.Errorf("failed to commit tx for saving keys into db %s: %w", dbPath, err)
|
|
|
|
}
|
|
|
|
return nil
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
|
|
|
|
2020-08-12 08:16:18 -07:00
|
|
|
func (am *AirgappedMachine) getParticipantID(dkgIdentifier string) (int, error) {
|
|
|
|
dkgInstance, ok := am.dkgInstances[dkgIdentifier]
|
|
|
|
if !ok {
|
|
|
|
return 0, fmt.Errorf("invalid dkg identifier: %s", dkgIdentifier)
|
|
|
|
}
|
|
|
|
return dkgInstance.ParticipantID, nil
|
|
|
|
}
|
|
|
|
|
2020-08-11 06:42:32 -07:00
|
|
|
func (am *AirgappedMachine) encryptData(dkgIdentifier, to string, data []byte) ([]byte, error) {
|
|
|
|
dkgInstance, ok := am.dkgInstances[dkgIdentifier]
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("invalid dkg identifier: %s", dkgIdentifier)
|
|
|
|
}
|
|
|
|
|
2020-08-13 08:27:28 -07:00
|
|
|
pk, err := dkgInstance.GetPubKeyByParticipant(to)
|
2020-08-11 06:42:32 -07:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to get pk for participant %s: %w", to, err)
|
|
|
|
}
|
|
|
|
|
2020-08-14 03:55:01 -07:00
|
|
|
encryptedData, err := ecies.Encrypt(am.suite, pk, data, am.suite.Hash)
|
2020-08-11 06:42:32 -07:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to encrypt data: %w", err)
|
|
|
|
}
|
|
|
|
return encryptedData, nil
|
|
|
|
}
|
|
|
|
|
2020-08-13 08:27:28 -07:00
|
|
|
func (am *AirgappedMachine) decryptData(data []byte) ([]byte, error) {
|
2020-08-14 03:55:01 -07:00
|
|
|
decryptedData, err := ecies.Decrypt(am.suite, am.secKey, data, am.suite.Hash)
|
2020-08-11 06:42:32 -07:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to decrypt data: %w", err)
|
|
|
|
}
|
|
|
|
return decryptedData, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (am *AirgappedMachine) handleStateAwaitParticipantsConfirmations(o *client.Operation) error {
|
|
|
|
var (
|
|
|
|
payload responses.SignatureProposalParticipantInvitationsResponse
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
2020-08-13 09:34:38 -07:00
|
|
|
if _, ok := am.dkgInstances[o.DKGIdentifier]; ok {
|
|
|
|
return fmt.Errorf("dkg instance %s already exists", o.DKGIdentifier)
|
|
|
|
}
|
|
|
|
|
2020-08-11 06:42:32 -07:00
|
|
|
if err = json.Unmarshal(o.Payload, &payload); err != nil {
|
|
|
|
return fmt.Errorf("failed to unmarshal payload: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := am.dkgInstances[o.DKGIdentifier]; ok {
|
|
|
|
return fmt.Errorf("dkg instance %s already exists", o.DKGIdentifier)
|
|
|
|
}
|
|
|
|
|
2020-08-13 08:27:28 -07:00
|
|
|
dkgInstance := dkg.Init(am.suite, am.pubKey, am.secKey)
|
2020-08-12 04:40:40 -07:00
|
|
|
dkgInstance.Threshold = len(payload)
|
2020-08-11 06:42:32 -07:00
|
|
|
|
2020-08-13 08:27:28 -07:00
|
|
|
am.dkgInstances[o.DKGIdentifier] = dkgInstance
|
2020-08-11 06:42:32 -07:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-08-13 08:27:28 -07:00
|
|
|
func (am *AirgappedMachine) GetPubKey() kyber.Point {
|
|
|
|
return am.pubKey
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
|
|
|
|
2020-08-14 03:55:01 -07:00
|
|
|
type Commits struct {
|
|
|
|
MarshaledCommit []byte
|
|
|
|
}
|
|
|
|
|
2020-08-10 09:00:29 -07:00
|
|
|
func (am *AirgappedMachine) handleStateDkgCommitsAwaitConfirmations(o *client.Operation) error {
|
|
|
|
var (
|
2020-08-13 09:34:38 -07:00
|
|
|
payload responses.SignatureProposalParticipantStatusResponse
|
2020-08-10 09:00:29 -07:00
|
|
|
err error
|
|
|
|
)
|
2020-08-11 02:46:13 -07:00
|
|
|
|
|
|
|
dkgInstance, ok := am.dkgInstances[o.DKGIdentifier]
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("dkg instance with identifier %s does not exist", o.DKGIdentifier)
|
|
|
|
}
|
|
|
|
|
2020-08-10 09:00:29 -07:00
|
|
|
if err = json.Unmarshal(o.Payload, &payload); err != nil {
|
|
|
|
return fmt.Errorf("failed to unmarshal payload: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, entry := range payload {
|
|
|
|
pubKey := bn256.NewSuiteG2().Point()
|
2020-08-13 09:34:38 -07:00
|
|
|
if err = pubKey.UnmarshalBinary(entry.DkgPubKey); err != nil {
|
2020-08-10 09:00:29 -07:00
|
|
|
return fmt.Errorf("failed to unmarshal pubkey: %w", err)
|
|
|
|
}
|
2020-08-13 09:34:38 -07:00
|
|
|
dkgInstance.StorePubKey(entry.Title, entry.ParticipantId, pubKey)
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
|
|
|
|
2020-08-12 04:40:40 -07:00
|
|
|
if err = dkgInstance.InitDKGInstance(); err != nil {
|
2020-08-10 09:00:29 -07:00
|
|
|
return fmt.Errorf("failed to init dkg instance: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-08-14 03:55:01 -07:00
|
|
|
// TODO: come up with something better
|
|
|
|
var commits []Commits
|
|
|
|
dkgCommits := dkgInstance.GetCommits()
|
|
|
|
for _, commit := range dkgCommits {
|
|
|
|
commitBz, err := commit.MarshalBinary()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to marshal commits: %w", err)
|
|
|
|
}
|
|
|
|
commits = append(commits, Commits{MarshaledCommit: commitBz})
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
2020-08-14 03:55:01 -07:00
|
|
|
commitsBz, err := json.Marshal(commits)
|
2020-08-10 09:00:29 -07:00
|
|
|
|
2020-08-11 02:46:13 -07:00
|
|
|
am.dkgInstances[o.DKGIdentifier] = dkgInstance
|
|
|
|
|
2020-08-10 09:00:29 -07:00
|
|
|
req := requests.DKGProposalCommitConfirmationRequest{
|
2020-08-11 02:46:13 -07:00
|
|
|
ParticipantId: dkgInstance.ParticipantID,
|
2020-08-14 03:55:01 -07:00
|
|
|
Commit: commitsBz,
|
2020-08-12 10:50:56 -07:00
|
|
|
CreatedAt: o.CreatedAt,
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
2020-08-14 03:55:01 -07:00
|
|
|
reqBz, err := json.Marshal(req)
|
2020-08-10 09:00:29 -07:00
|
|
|
if err != nil {
|
2020-08-12 08:16:18 -07:00
|
|
|
return fmt.Errorf("failed to generate fsm request: %w", err)
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
|
|
|
o.Result = reqBz
|
2020-08-14 03:55:01 -07:00
|
|
|
o.Event = dkg_proposal_fsm.EventDKGCommitConfirmationReceived
|
2020-08-10 09:00:29 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-08-12 08:16:18 -07:00
|
|
|
// We have many deals which should be sent privately to a required participant, so func returns a slice of operations
|
2020-08-14 03:55:01 -07:00
|
|
|
func (am *AirgappedMachine) handleStateDkgDealsAwaitConfirmations(o client.Operation) ([]client.Operation, error) {
|
2020-08-10 09:00:29 -07:00
|
|
|
var (
|
|
|
|
payload responses.DKGProposalCommitParticipantResponse
|
|
|
|
err error
|
|
|
|
)
|
2020-08-11 02:46:13 -07:00
|
|
|
|
|
|
|
dkgInstance, ok := am.dkgInstances[o.DKGIdentifier]
|
|
|
|
if !ok {
|
2020-08-11 06:42:32 -07:00
|
|
|
return nil, fmt.Errorf("dkg instance with identifier %s does not exist", o.DKGIdentifier)
|
2020-08-11 02:46:13 -07:00
|
|
|
}
|
|
|
|
|
2020-08-10 09:00:29 -07:00
|
|
|
if err = json.Unmarshal(o.Payload, &payload); err != nil {
|
2020-08-11 06:42:32 -07:00
|
|
|
return nil, fmt.Errorf("failed to unmarshal payload: %w", err)
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, entry := range payload {
|
2020-08-14 03:55:01 -07:00
|
|
|
var commits []Commits
|
2020-08-10 09:00:29 -07:00
|
|
|
if err = json.Unmarshal(entry.Commit, &commits); err != nil {
|
2020-08-11 06:42:32 -07:00
|
|
|
return nil, fmt.Errorf("failed to unmarshal commits: %w", err)
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
2020-08-14 03:55:01 -07:00
|
|
|
dkgCommits := make([]kyber.Point, 0, len(commits))
|
|
|
|
for _, c := range commits {
|
|
|
|
commit := am.suite.Point()
|
|
|
|
if err = commit.UnmarshalBinary(c.MarshaledCommit); err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to unmarshal commit: %w", err)
|
|
|
|
}
|
|
|
|
dkgCommits = append(dkgCommits, commit)
|
|
|
|
}
|
|
|
|
dkgInstance.StoreCommits(entry.Title, dkgCommits)
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
|
|
|
|
2020-08-11 02:46:13 -07:00
|
|
|
deals, err := dkgInstance.GetDeals()
|
2020-08-10 09:00:29 -07:00
|
|
|
if err != nil {
|
2020-08-11 06:42:32 -07:00
|
|
|
return nil, fmt.Errorf("failed to get deals: %w", err)
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
|
|
|
|
2020-08-11 06:42:32 -07:00
|
|
|
operations := make([]client.Operation, 0, len(deals))
|
2020-08-10 09:00:29 -07:00
|
|
|
|
2020-08-11 06:42:32 -07:00
|
|
|
am.dkgInstances[o.DKGIdentifier] = dkgInstance
|
2020-08-10 09:00:29 -07:00
|
|
|
|
2020-08-12 08:16:18 -07:00
|
|
|
// deals variable is a map, so every key is an index of participant we should send a deal
|
2020-08-11 06:42:32 -07:00
|
|
|
for index, deal := range deals {
|
|
|
|
dealBz, err := json.Marshal(deal)
|
|
|
|
if err != nil {
|
2020-08-12 08:16:18 -07:00
|
|
|
return nil, fmt.Errorf("failed to marshal deal: %w", err)
|
2020-08-11 06:42:32 -07:00
|
|
|
}
|
|
|
|
toParticipant := dkgInstance.GetParticipantByIndex(index)
|
|
|
|
encryptedDeal, err := am.encryptData(o.DKGIdentifier, toParticipant, dealBz)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to encrypt deal: %w", err)
|
|
|
|
}
|
|
|
|
req := requests.DKGProposalDealConfirmationRequest{
|
|
|
|
ParticipantId: dkgInstance.ParticipantID,
|
|
|
|
Deal: encryptedDeal,
|
2020-08-12 10:50:56 -07:00
|
|
|
CreatedAt: o.CreatedAt,
|
2020-08-11 06:42:32 -07:00
|
|
|
}
|
|
|
|
o.To = toParticipant
|
2020-08-14 03:55:01 -07:00
|
|
|
reqBz, err := json.Marshal(req)
|
2020-08-11 06:42:32 -07:00
|
|
|
if err != nil {
|
2020-08-12 08:16:18 -07:00
|
|
|
return nil, fmt.Errorf("failed to generate fsm request: %w", err)
|
2020-08-11 06:42:32 -07:00
|
|
|
}
|
|
|
|
o.Result = reqBz
|
2020-08-14 03:55:01 -07:00
|
|
|
o.Event = dkg_proposal_fsm.EventDKGDealConfirmationReceived
|
|
|
|
operations = append(operations, o)
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
2020-08-11 06:42:32 -07:00
|
|
|
return operations, nil
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (am *AirgappedMachine) handleStateDkgResponsesAwaitConfirmations(o *client.Operation) error {
|
|
|
|
var (
|
|
|
|
payload responses.DKGProposalDealParticipantResponse
|
|
|
|
err error
|
|
|
|
)
|
2020-08-11 02:46:13 -07:00
|
|
|
|
|
|
|
dkgInstance, ok := am.dkgInstances[o.DKGIdentifier]
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("dkg instance with identifier %s does not exist", o.DKGIdentifier)
|
|
|
|
}
|
|
|
|
|
2020-08-10 09:00:29 -07:00
|
|
|
if err = json.Unmarshal(o.Payload, &payload); err != nil {
|
|
|
|
return fmt.Errorf("failed to unmarshal payload: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, entry := range payload {
|
2020-08-13 08:27:28 -07:00
|
|
|
decryptedDealBz, err := am.decryptData(entry.Deal)
|
2020-08-11 07:29:19 -07:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to decrypt deal: %w", err)
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
2020-08-11 07:29:19 -07:00
|
|
|
var deal dkgPedersen.Deal
|
|
|
|
if err = json.Unmarshal(decryptedDealBz, &deal); err != nil {
|
|
|
|
return fmt.Errorf("failed to unmarshal deal")
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
2020-08-11 07:29:19 -07:00
|
|
|
dkgInstance.StoreDeal(entry.Title, &deal)
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
|
|
|
|
2020-08-11 02:46:13 -07:00
|
|
|
processedResponses, err := dkgInstance.ProcessDeals()
|
2020-08-10 09:00:29 -07:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to process deals: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-08-11 02:46:13 -07:00
|
|
|
am.dkgInstances[o.DKGIdentifier] = dkgInstance
|
|
|
|
|
2020-08-10 09:00:29 -07:00
|
|
|
responsesBz, err := json.Marshal(processedResponses)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to marshal deals")
|
|
|
|
}
|
|
|
|
|
|
|
|
req := requests.DKGProposalResponseConfirmationRequest{
|
2020-08-11 02:46:13 -07:00
|
|
|
ParticipantId: dkgInstance.ParticipantID,
|
2020-08-10 09:00:29 -07:00
|
|
|
Response: responsesBz,
|
2020-08-12 10:50:56 -07:00
|
|
|
CreatedAt: o.CreatedAt,
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
|
|
|
|
2020-08-14 03:55:01 -07:00
|
|
|
reqBz, err := json.Marshal(req)
|
2020-08-10 09:00:29 -07:00
|
|
|
if err != nil {
|
2020-08-12 08:16:18 -07:00
|
|
|
return fmt.Errorf("failed to generate fsm request: %w", err)
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
2020-08-12 08:16:18 -07:00
|
|
|
|
2020-08-10 09:00:29 -07:00
|
|
|
o.Result = reqBz
|
2020-08-14 03:55:01 -07:00
|
|
|
o.Event = dkg_proposal_fsm.EventDKGResponseConfirmationReceived
|
2020-08-10 09:00:29 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-08-12 10:50:56 -07:00
|
|
|
func (am *AirgappedMachine) handleStateDkgMasterKeyAwaitConfirmations(o *client.Operation) error {
|
|
|
|
var (
|
|
|
|
payload responses.DKGProposalResponsesParticipantResponse
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
dkgInstance, ok := am.dkgInstances[o.DKGIdentifier]
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("dkg instance with identifier %s does not exist", o.DKGIdentifier)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = json.Unmarshal(o.Payload, &payload); err != nil {
|
|
|
|
return fmt.Errorf("failed to unmarshal payload: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, entry := range payload {
|
|
|
|
var entryResponses []*dkgPedersen.Response
|
|
|
|
if err = json.Unmarshal(entry.Responses, &entryResponses); err != nil {
|
|
|
|
return fmt.Errorf("failed to unmarshal responses: %w", err)
|
|
|
|
}
|
|
|
|
dkgInstance.StoreResponses(entry.Title, entryResponses)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = dkgInstance.ProcessResponses(); err != nil {
|
|
|
|
return fmt.Errorf("failed to process responses: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-08-14 07:38:58 -07:00
|
|
|
masterPubKey, err := dkgInstance.GetDistributedPublicKey()
|
2020-08-12 10:50:56 -07:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to get master pub key: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-08-14 07:38:58 -07:00
|
|
|
masterPubKeyBz, err := masterPubKey.MarshalBinary()
|
2020-08-12 10:50:56 -07:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to marshal master pub key: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
am.dkgInstances[o.DKGIdentifier] = dkgInstance
|
|
|
|
|
|
|
|
req := requests.DKGProposalMasterKeyConfirmationRequest{
|
|
|
|
ParticipantId: dkgInstance.ParticipantID,
|
|
|
|
MasterKey: masterPubKeyBz,
|
|
|
|
CreatedAt: o.CreatedAt,
|
|
|
|
}
|
2020-08-14 03:55:01 -07:00
|
|
|
reqBz, err := json.Marshal(req)
|
2020-08-12 10:50:56 -07:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to generate fsm request: %w", err)
|
|
|
|
}
|
2020-08-14 04:16:18 -07:00
|
|
|
|
2020-08-12 10:50:56 -07:00
|
|
|
o.Result = reqBz
|
2020-08-14 03:55:01 -07:00
|
|
|
o.Event = dkg_proposal_fsm.EventDKGMasterKeyConfirmationReceived
|
2020-08-12 10:50:56 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-08-13 08:27:28 -07:00
|
|
|
func (am *AirgappedMachine) HandleOperation(operation client.Operation) ([]client.Operation, error) {
|
2020-08-10 09:00:29 -07:00
|
|
|
var (
|
2020-08-12 08:16:18 -07:00
|
|
|
err error
|
|
|
|
// output operations (cause of deals)
|
2020-08-11 06:42:32 -07:00
|
|
|
operations []client.Operation
|
2020-08-10 09:00:29 -07:00
|
|
|
)
|
|
|
|
|
2020-08-11 06:42:32 -07:00
|
|
|
am.Lock()
|
|
|
|
defer am.Unlock()
|
|
|
|
|
2020-08-12 08:16:18 -07:00
|
|
|
// handler gets a pointer to an operation, do necessary things
|
|
|
|
// and write a result (or an error) to .Result field of operation
|
2020-08-10 09:00:29 -07:00
|
|
|
switch fsm.State(operation.Type) {
|
2020-08-11 06:42:32 -07:00
|
|
|
case signature_proposal_fsm.StateAwaitParticipantsConfirmations:
|
2020-08-12 08:16:18 -07:00
|
|
|
err = am.handleStateAwaitParticipantsConfirmations(&operation)
|
2020-08-10 09:00:29 -07:00
|
|
|
case dkg_proposal_fsm.StateDkgCommitsAwaitConfirmations:
|
2020-08-12 08:16:18 -07:00
|
|
|
err = am.handleStateDkgCommitsAwaitConfirmations(&operation)
|
2020-08-10 09:00:29 -07:00
|
|
|
case dkg_proposal_fsm.StateDkgDealsAwaitConfirmations:
|
2020-08-14 03:55:01 -07:00
|
|
|
operations, err = am.handleStateDkgDealsAwaitConfirmations(operation)
|
2020-08-10 09:00:29 -07:00
|
|
|
case dkg_proposal_fsm.StateDkgResponsesAwaitConfirmations:
|
2020-08-12 08:16:18 -07:00
|
|
|
err = am.handleStateDkgResponsesAwaitConfirmations(&operation)
|
2020-08-12 10:50:56 -07:00
|
|
|
case dkg_proposal_fsm.StateDkgMasterKeyAwaitConfirmations:
|
|
|
|
err = am.handleStateDkgMasterKeyAwaitConfirmations(&operation)
|
2020-08-10 09:00:29 -07:00
|
|
|
default:
|
2020-08-12 08:16:18 -07:00
|
|
|
err = fmt.Errorf("invalid operation type: %s", operation.Type)
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we have error after handling the operation, we write the error to the operation, so we can feed it to a FSM
|
|
|
|
if err != nil {
|
2020-08-14 03:55:01 -07:00
|
|
|
log.Println(fmt.Sprintf("failed to handle operation %s, returning response with errot to client: %v",
|
|
|
|
operation.Type, err))
|
2020-08-12 08:16:18 -07:00
|
|
|
if e := am.writeErrorRequestToOperation(&operation, err); e != nil {
|
|
|
|
return nil, fmt.Errorf("failed to write error request to an operation: %w", e)
|
|
|
|
}
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
|
|
|
|
2020-08-11 06:42:32 -07:00
|
|
|
if len(operation.Result) > 0 {
|
|
|
|
operations = append(operations, operation)
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
|
|
|
|
2020-08-13 08:27:28 -07:00
|
|
|
return operations, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// HandleQR - gets an operation from a QR code, do necessary things for the operation and returns paths to QR-code images
|
|
|
|
func (am *AirgappedMachine) HandleQR() ([]string, error) {
|
|
|
|
var (
|
|
|
|
err error
|
|
|
|
|
|
|
|
// input operation
|
|
|
|
operation client.Operation
|
|
|
|
qrData []byte
|
|
|
|
|
|
|
|
// output operations (cause of deals)
|
|
|
|
operations []client.Operation
|
|
|
|
)
|
|
|
|
|
|
|
|
if qrData, err = am.qrProcessor.ReadQR(); err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to read QR: %w", err)
|
|
|
|
}
|
|
|
|
if err = json.Unmarshal(qrData, &operation); err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to unmarshal operation: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if operations, err = am.HandleOperation(operation); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-08-11 06:42:32 -07:00
|
|
|
qrPath := "%s/%s_%s_%s.png"
|
|
|
|
qrPaths := make([]string, 0, len(operations))
|
|
|
|
for _, o := range operations {
|
|
|
|
operationBz, err := json.Marshal(o)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to marshal operation: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-08-12 11:01:25 -07:00
|
|
|
if err = am.qrProcessor.WriteQR(fmt.Sprintf(qrPath, resultQRFolder, o.Type, o.ID, o.To), operationBz); err != nil {
|
2020-08-11 06:42:32 -07:00
|
|
|
return nil, fmt.Errorf("failed to write QR")
|
|
|
|
}
|
|
|
|
qrPaths = append(qrPaths, qrPath)
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
|
|
|
|
2020-08-11 06:42:32 -07:00
|
|
|
return qrPaths, nil
|
2020-08-10 09:00:29 -07:00
|
|
|
}
|
2020-08-12 08:16:18 -07:00
|
|
|
|
2020-08-14 07:38:58 -07:00
|
|
|
func (am *AirgappedMachine) handlePartialSign(msg []byte, dkgIdentifier string) ([]byte, error) {
|
|
|
|
dkgInstance, ok := am.dkgInstances[dkgIdentifier]
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("dkg instance with identifier %s does not exist", dkgIdentifier)
|
|
|
|
}
|
|
|
|
|
|
|
|
distKeyShare, err := dkgInstance.GetDistKeyShare()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return am.makePartialSign(msg, distKeyShare.PriShare())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (am *AirgappedMachine) handleRecoverFullSign(msg []byte, sigShares [][]byte, dkgIdentifier string) ([]byte, error) {
|
|
|
|
dkgInstance, ok := am.dkgInstances[dkgIdentifier]
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("dkg instance with identifier %s does not exist", dkgIdentifier)
|
|
|
|
}
|
|
|
|
|
|
|
|
masterKey, err := dkgInstance.GetMasterPubKey()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return am.recoverFullSignature(msg, masterKey, sigShares, len(sigShares), len(sigShares))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (am *AirgappedMachine) handleVerifySign(msg []byte, fullSignature []byte, dkgIdentifier string) error {
|
|
|
|
dkgInstance, ok := am.dkgInstances[dkgIdentifier]
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("dkg instance with identifier %s does not exist", dkgIdentifier)
|
|
|
|
}
|
|
|
|
|
|
|
|
masterKey, err := dkgInstance.GetMasterPubKey()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return bls.Verify(am.suite, masterKey.Commit(), msg, fullSignature)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (am *AirgappedMachine) makePartialSign(msg []byte, share *share.PriShare) ([]byte, error) {
|
|
|
|
return tbls.Sign(am.suite, share, msg)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (am *AirgappedMachine) recoverFullSignature(msg []byte, pubPoly *share.PubPoly,
|
|
|
|
sigShares [][]byte, t, n int) ([]byte, error) {
|
|
|
|
return tbls.Recover(am.suite, pubPoly, msg, sigShares, t, n)
|
|
|
|
}
|
|
|
|
|
2020-08-12 08:16:18 -07:00
|
|
|
func (am *AirgappedMachine) writeErrorRequestToOperation(o *client.Operation, handlerError error) error {
|
|
|
|
// each type of request should have a required event even error
|
|
|
|
// maybe should be global?
|
|
|
|
eventToErrorMap := map[fsm.State]fsm.Event{
|
|
|
|
dkg_proposal_fsm.StateDkgCommitsAwaitConfirmations: dkg_proposal_fsm.EventDKGCommitConfirmationError,
|
|
|
|
dkg_proposal_fsm.StateDkgDealsAwaitConfirmations: dkg_proposal_fsm.EventDKGDealConfirmationError,
|
|
|
|
dkg_proposal_fsm.StateDkgResponsesAwaitConfirmations: dkg_proposal_fsm.EventDKGResponseConfirmationError,
|
2020-08-12 10:50:56 -07:00
|
|
|
dkg_proposal_fsm.StateDkgMasterKeyAwaitConfirmations: dkg_proposal_fsm.EventDKGMasterKeyConfirmationError,
|
2020-08-12 08:16:18 -07:00
|
|
|
}
|
|
|
|
pid, err := am.getParticipantID(o.DKGIdentifier)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to get participant id: %w", err)
|
|
|
|
}
|
|
|
|
req := requests.DKGProposalConfirmationErrorRequest{
|
|
|
|
Error: handlerError,
|
|
|
|
ParticipantId: pid,
|
2020-08-12 10:50:56 -07:00
|
|
|
CreatedAt: o.CreatedAt,
|
2020-08-12 08:16:18 -07:00
|
|
|
}
|
|
|
|
errorEvent := eventToErrorMap[fsm.State(o.Type)]
|
2020-08-14 03:55:01 -07:00
|
|
|
reqBz, err := json.Marshal(req)
|
2020-08-12 08:16:18 -07:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to generate fsm request: %w", err)
|
|
|
|
}
|
|
|
|
o.Result = reqBz
|
2020-08-14 03:55:01 -07:00
|
|
|
o.Event = errorEvent
|
2020-08-12 08:16:18 -07:00
|
|
|
return nil
|
|
|
|
}
|