saving signatures on the client for further use and verification

This commit is contained in:
programmer10110 2020-10-05 13:58:52 +03:00
parent ba8ee3978f
commit d6259d10d9
10 changed files with 351 additions and 12 deletions

View File

@ -70,6 +70,8 @@ func (n *Node) storeOperation(t *testing.T, msg storage.Message) {
t.Fatalf("failed to unmarshal fsm req: %v", err)
}
n.partialSigns = append(n.partialSigns, req)
case client.SignatureReconstructed:
return
default:
t.Fatalf("invalid event: %s", msg.Event)
}

View File

@ -1,7 +1,6 @@
package airgapped
import (
"encoding/base64"
"encoding/json"
"fmt"
@ -107,7 +106,18 @@ func (am *AirgappedMachine) reconstructThresholdSignature(o *client.Operation) e
if err != nil {
return fmt.Errorf("failed to reconsruct full signature for msg: %w", err)
}
fmt.Println(base64.StdEncoding.EncodeToString(reconstructedSignature))
response := client.ReconstructedSignature{
Data: payload.SrcPayload,
Signature: reconstructedSignature,
DKGRoundID: o.DKGIdentifier,
}
respBz, err := json.Marshal(response)
if err != nil {
return fmt.Errorf("failed to generate reconstructed signature response: %w", err)
}
o.Event = client.SignatureReconstructed
o.ResultMsgs = append(o.ResultMsgs, createMessage(*o, respBz))
return nil
}

View File

@ -136,7 +136,30 @@ func (c *Client) SendMessage(message storage.Message) error {
return nil
}
// processSignature saves a broadcasted reconstructed signature to a LevelDB
func (c *Client) processSignature(message storage.Message) error {
var (
signature types.ReconstructedSignature
err error
)
if err = json.Unmarshal(message.Data, &signature); err != nil {
return fmt.Errorf("failed to unmarshal reconstructed signature: %w", err)
}
signature.Participant = message.SenderAddr
return c.state.SaveSignature(signature)
}
func (c *Client) ProcessMessage(message storage.Message) error {
if fsm.Event(message.Event) == types.SignatureReconstructed {
if err := c.processSignature(message); err != nil {
return fmt.Errorf("failed to process signature: %w", err)
}
if err := c.state.SaveOffset(message.Offset + 1); err != nil {
return fmt.Errorf("failed to SaveOffset: %w", err)
}
return nil
}
fsmInstance, err := c.getFSMInstance(message.DkgRoundID)
if err != nil {
return fmt.Errorf("failed to getFSMInstance: %w", err)
@ -264,6 +287,17 @@ func (c *Client) GetOperations() (map[string]*types.Operation, error) {
return c.state.GetOperations()
}
//GetSignatures returns all signatures for the given DKG round that were reconstructed on the airgapped machine and
// broadcasted by users
func (c *Client) GetSignatures(dkgID string) (map[string][]types.ReconstructedSignature, error) {
return c.state.GetSignatures(dkgID)
}
//GetSignatureByDataHash returns a list of reconstructed signatures of the signed data broadcasted by users
func (c *Client) GetSignatureByDataHash(dkgID, sigID string) ([]types.ReconstructedSignature, error) {
return c.state.GetSignatureByDataHash(dkgID, sigID)
}
// getOperationJSON returns a specific JSON-encoded operation
func (c *Client) getOperationJSON(operationID string) ([]byte, error) {
operation, err := c.state.GetOperationByID(operationID)

View File

@ -68,6 +68,9 @@ func (c *Client) StartHTTPServer(listenAddr string) error {
mux.HandleFunc("/getOperations", c.getOperationsHandler)
mux.HandleFunc("/getOperationQRPath", c.getOperationQRPathHandler)
mux.HandleFunc("/getSignatures", c.getSignaturesHandler)
mux.HandleFunc("/getSignatureByDataHash", c.getSignatureByDataHashHandler)
mux.HandleFunc("/getOperationQR", c.getOperationQRToBodyHandler)
mux.HandleFunc("/handleProcessedOperationJSON", c.handleJSONOperationHandler)
mux.HandleFunc("/getOperation", c.getOperationHandler)
@ -136,6 +139,36 @@ func (c *Client) getOperationsHandler(w http.ResponseWriter, r *http.Request) {
successResponse(w, operations)
}
func (c *Client) getSignaturesHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
errorResponse(w, http.StatusBadRequest, "Wrong HTTP method")
return
}
signatures, err := c.GetSignatures(r.URL.Query().Get("dkgID"))
if err != nil {
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to get signatures: %v", err))
return
}
successResponse(w, signatures)
}
func (c *Client) getSignatureByDataHashHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
errorResponse(w, http.StatusBadRequest, "Wrong HTTP method")
return
}
signature, err := c.GetSignatureByDataHash(r.URL.Query().Get("dkgID"), r.URL.Query().Get("hash"))
if err != nil {
errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to get signature: %v", err))
return
}
successResponse(w, signature)
}
func (c *Client) getOperationQRPathHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
errorResponse(w, http.StatusBadRequest, "Wrong HTTP method")

View File

@ -1,7 +1,9 @@
package client
import (
"crypto/md5"
"encoding/binary"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
@ -15,9 +17,10 @@ import (
)
const (
offsetKey = "offset"
operationsKey = "operations"
fsmStateKey = "fsm_state"
offsetKey = "offset"
operationsKey = "operations"
fsmStateKey = "fsm_state"
signaturesKeyPrefix = "signatures"
)
type State interface {
@ -31,6 +34,10 @@ type State interface {
DeleteOperation(operationID string) error
GetOperations() (map[string]*types.Operation, error)
GetOperationByID(operationID string) (*types.Operation, error)
SaveSignature(signature types.ReconstructedSignature) error
GetSignatureByDataHash(dkgID, signatureID string) ([]types.ReconstructedSignature, error)
GetSignatures(dkgID string) (map[string][]types.ReconstructedSignature, error)
}
type LevelDBState struct {
@ -251,3 +258,79 @@ func (s *LevelDBState) getOperations() (map[string]*types.Operation, error) {
return operations, nil
}
func makeSignatureKey(dkgID string) []byte {
return []byte(fmt.Sprintf("%s_%s", signaturesKeyPrefix, dkgID))
}
func (s *LevelDBState) getSignatures(dkgID string) (map[string][]types.ReconstructedSignature, error) {
bz, err := s.stateDb.Get(makeSignatureKey(dkgID), nil)
if err != nil {
if err == leveldb.ErrNotFound {
return nil, nil
}
return nil, fmt.Errorf("failed to get signatures for dkgID %s: %w", dkgID, err)
}
var signatures map[string][]types.ReconstructedSignature
if err := json.Unmarshal(bz, &signatures); err != nil {
return nil, fmt.Errorf("failed to unmarshal Operations: %w", err)
}
return signatures, nil
}
func (s *LevelDBState) GetSignatures(dkgID string) (map[string][]types.ReconstructedSignature, error) {
s.Lock()
defer s.Unlock()
return s.getSignatures(dkgID)
}
func (s *LevelDBState) GetSignatureByDataHash(dkgID, signatureID string) ([]types.ReconstructedSignature, error) {
s.Lock()
defer s.Unlock()
signatures, err := s.getSignatures(dkgID)
if err != nil {
return nil, fmt.Errorf("failed to getSignatures: %w", err)
}
signature, ok := signatures[signatureID]
if !ok {
return nil, errors.New("signature not found")
}
return signature, nil
}
func (s *LevelDBState) SaveSignature(signature types.ReconstructedSignature) error {
s.Lock()
defer s.Unlock()
signatures, err := s.getSignatures(signature.DKGRoundID)
if err != nil {
return fmt.Errorf("failed to getSignatures: %w", err)
}
if signatures == nil {
signatures = make(map[string][]types.ReconstructedSignature)
}
dataHash := md5.Sum(signature.Data)
dataHashString := hex.EncodeToString(dataHash[:])
sig := signatures[dataHashString]
sig = append(sig, signature)
signatures[dataHashString] = sig
signaturesJSON, err := json.Marshal(signatures)
if err != nil {
return fmt.Errorf("failed to marshal signatures: %w", err)
}
if err := s.stateDb.Put(makeSignatureKey(signature.DKGRoundID), signaturesJSON, nil); err != nil {
return fmt.Errorf("failed to save signatures: %w", err)
}
return nil
}

View File

@ -17,9 +17,17 @@ import (
type OperationType string
const (
DKGCommits OperationType = "dkg_commits"
DKGCommits OperationType = "dkg_commits"
SignatureReconstructed fsm.Event = "signature_reconstructed"
)
type ReconstructedSignature struct {
Data []byte
Signature []byte
Participant string
DKGRoundID string
}
type Operation struct {
ID string // UUID4
Type OperationType

View File

@ -3,6 +3,7 @@ package main
import (
"bytes"
"crypto/md5"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
@ -53,6 +54,8 @@ func main() {
getUsernameCommand(),
getPubKeyCommand(),
getHashOfStartDKGCommand(),
getSignaturesCommand(),
getSignatureCommand(),
)
if err := rootCmd.Execute(); err != nil {
log.Fatalf("Failed to execute root command: %v", err)
@ -119,6 +122,100 @@ func getOperationsCommand() *cobra.Command {
}
}
func getSignaturesRequest(host string, dkgID string) (*SignaturesResponse, error) {
resp, err := http.Get(fmt.Sprintf("http://%s/getSignatures?dkgID=%s", host, dkgID))
if err != nil {
return nil, fmt.Errorf("failed to get signatures: %w", err)
}
defer resp.Body.Close()
responseBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read body: %w", err)
}
var response SignaturesResponse
if err = json.Unmarshal(responseBody, &response); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
}
return &response, nil
}
func getSignaturesCommand() *cobra.Command {
return &cobra.Command{
Use: "get_signatures [dkgID]",
Args: cobra.ExactArgs(1),
Short: "returns all signatures for the given DKG round that were reconstructed on the airgapped machine",
RunE: func(cmd *cobra.Command, args []string) error {
listenAddr, err := cmd.Flags().GetString(flagListenAddr)
if err != nil {
return fmt.Errorf("failed to read configuration: %v", err)
}
signatures, err := getSignaturesRequest(listenAddr, args[0])
if err != nil {
return fmt.Errorf("failed to get signatures: %w", err)
}
if signatures.ErrorMessage != "" {
return fmt.Errorf("failed to get signatures: %s", signatures.ErrorMessage)
}
for dataHash, signature := range signatures.Result {
fmt.Printf("Hash of the signing data: %s\n", dataHash)
for _, participantSig := range signature {
fmt.Printf("\tDKG round ID: %s\n", participantSig.DKGRoundID)
fmt.Printf("\tParticipant: %s\n", participantSig.Participant)
fmt.Printf("\tReconstructed signature for the data: %s\n", base64.StdEncoding.EncodeToString(participantSig.Signature))
fmt.Println()
}
}
return nil
},
}
}
func getSignatureRequest(host string, dkgID, dataHash string) (*SignatureResponse, error) {
resp, err := http.Get(fmt.Sprintf("http://%s/getSignatureByDataHash?dkgID=%s&hash=%s", host, dkgID, dataHash))
if err != nil {
return nil, fmt.Errorf("failed to get signatures: %w", err)
}
defer resp.Body.Close()
responseBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read body: %w", err)
}
var response SignatureResponse
if err = json.Unmarshal(responseBody, &response); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %v", err)
}
return &response, nil
}
func getSignatureCommand() *cobra.Command {
return &cobra.Command{
Use: "get_signature [dkgID] [hash_of_the_signed_data]",
Args: cobra.ExactArgs(2),
Short: "returns a list of reconstructed signatures of the signed data broadcasted by users",
RunE: func(cmd *cobra.Command, args []string) error {
listenAddr, err := cmd.Flags().GetString(flagListenAddr)
if err != nil {
return fmt.Errorf("failed to read configuration: %v", err)
}
signatures, err := getSignatureRequest(listenAddr, args[0], args[1])
if err != nil {
return fmt.Errorf("failed to get signatures: %w", err)
}
if signatures.ErrorMessage != "" {
return fmt.Errorf("failed to get signatures: %s", signatures.ErrorMessage)
}
for _, participantSig := range signatures.Result {
fmt.Printf("\tParticipant: %s\n", participantSig.Participant)
fmt.Printf("\tReconstructed signature for the data: %s\n", base64.StdEncoding.EncodeToString(participantSig.Signature))
fmt.Println()
}
return nil
},
}
}
func getOperationRequest(host string, operationID string) (*OperationResponse, error) {
resp, err := http.Get(fmt.Sprintf("http://%s/getOperation?operationID=%s", host, operationID))
if err != nil {

View File

@ -32,6 +32,16 @@ type OperationsResponse struct {
Result map[string]*types.Operation `json:"result"`
}
type SignaturesResponse struct {
ErrorMessage string `json:"error_message,omitempty"`
Result map[string][]types.ReconstructedSignature `json:"result"`
}
type SignatureResponse struct {
ErrorMessage string `json:"error_message,omitempty"`
Result []types.ReconstructedSignature `json:"result"`
}
type OperationResponse struct {
ErrorMessage string `json:"error_message,omitempty"`
Result []byte `json:"result"`

View File

@ -150,3 +150,47 @@ func (mr *MockStateMockRecorder) GetOperationByID(operationID interface{}) *gomo
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOperationByID", reflect.TypeOf((*MockState)(nil).GetOperationByID), operationID)
}
// SaveSignature mocks base method
func (m *MockState) SaveSignature(signature types.ReconstructedSignature) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SaveSignature", signature)
ret0, _ := ret[0].(error)
return ret0
}
// SaveSignature indicates an expected call of SaveSignature
func (mr *MockStateMockRecorder) SaveSignature(signature interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveSignature", reflect.TypeOf((*MockState)(nil).SaveSignature), signature)
}
// GetSignatureByDataHash mocks base method
func (m *MockState) GetSignatureByDataHash(dkgID, signatureID string) ([]types.ReconstructedSignature, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetSignatureByDataHash", dkgID, signatureID)
ret0, _ := ret[0].([]types.ReconstructedSignature)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetSignatureByDataHash indicates an expected call of GetSignatureByDataHash
func (mr *MockStateMockRecorder) GetSignatureByDataHash(dkgID, signatureID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSignatureByDataHash", reflect.TypeOf((*MockState)(nil).GetSignatureByDataHash), dkgID, signatureID)
}
// GetSignatures mocks base method
func (m *MockState) GetSignatures(dkgID string) (map[string][]types.ReconstructedSignature, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetSignatures", dkgID)
ret0, _ := ret[0].(map[string][]types.ReconstructedSignature)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetSignatures indicates an expected call of GetSignatures
func (mr *MockStateMockRecorder) GetSignatures(dkgID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSignatures", reflect.TypeOf((*MockState)(nil).GetSignatures), dkgID)
}

View File

@ -32,12 +32,6 @@ func (m *MockProcessor) EXPECT() *MockProcessorMockRecorder {
return m.recorder
}
func (m *MockProcessor) SetDelay(int) {
}
func (m *MockProcessor) SetChunkSize(int) {
}
// ReadQR mocks base method
func (m *MockProcessor) ReadQR() ([]byte, error) {
m.ctrl.T.Helper()
@ -66,3 +60,27 @@ func (mr *MockProcessorMockRecorder) WriteQR(path, data interface{}) *gomock.Cal
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteQR", reflect.TypeOf((*MockProcessor)(nil).WriteQR), path, data)
}
// SetDelay mocks base method
func (m *MockProcessor) SetDelay(delay int) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetDelay", delay)
}
// SetDelay indicates an expected call of SetDelay
func (mr *MockProcessorMockRecorder) SetDelay(delay interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDelay", reflect.TypeOf((*MockProcessor)(nil).SetDelay), delay)
}
// SetChunkSize mocks base method
func (m *MockProcessor) SetChunkSize(chunkSize int) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetChunkSize", chunkSize)
}
// SetChunkSize indicates an expected call of SetChunkSize
func (mr *MockProcessorMockRecorder) SetChunkSize(chunkSize interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetChunkSize", reflect.TypeOf((*MockProcessor)(nil).SetChunkSize), chunkSize)
}