starter.go 6.38 KB
Newer Older
1 2 3 4 5
package sign

import (
	"errors"
	"fmt"
6
	"log"
7
	"strconv"
8
	"sync"
Loïck Bonniot's avatar
Loïck Bonniot committed
9
	"time"
10

Caro Axel's avatar
Caro Axel committed
11 12
	"dfss"
	cAPI "dfss/dfssc/api"
13
	"dfss/dfssc/common"
14
	"dfss/dfssc/security"
Caro Axel's avatar
Caro Axel committed
15
	pAPI "dfss/dfssp/api"
16 17 18
	"dfss/dfssp/contract"
	"dfss/net"
	"golang.org/x/net/context"
Caro Axel's avatar
Caro Axel committed
19
	"google.golang.org/grpc"
20 21
)

22 23 24
// Limit the buffer size of the channels
const chanBufferSize = 100

25 26
// SignatureManager handles the signature of a contract.
type SignatureManager struct {
27 28 29 30
	auth         *security.AuthContainer
	localPort    int
	contract     *contract.JSON // contains the contractUUID, the list of the signers' hashes, the hash of the contract
	platform     pAPI.PlatformClient
31 32
	platformConn *grpc.ClientConn
	peersConn    map[string]*grpc.ClientConn
33
	peers        map[string]*cAPI.ClientClient
34
	hashToID     map[string]uint32
35 36
	nbReady      int
	cServer      *grpc.Server
37
	cServerIface clientServer
38 39 40 41 42 43
	sequence     []uint32
	currentIndex int
	uuid         string
	keyHash      [][]byte
	mail         string
	archives     *Archives
44 45
}

46
// Archives stores the received and sent messages, as evidence if needed
47 48
type Archives struct {
	sentPromises       []*cAPI.Promise
49
	receivedPromises   []*cAPI.Promise
50
	sentSignatures     []*cAPI.Signature
51
	receivedSignatures []*cAPI.Signature
52
	mutex              sync.Mutex
53 54 55 56 57 58 59 60
}

// NewSignatureManager populates a SignatureManager and connects to the platform.
func NewSignatureManager(fileCA, fileCert, fileKey, addrPort, passphrase string, port int, c *contract.JSON) (*SignatureManager, error) {
	m := &SignatureManager{
		auth:      security.NewAuthContainer(fileCA, fileCert, fileKey, addrPort, passphrase),
		localPort: port,
		contract:  c,
61 62
		archives: &Archives{
			sentPromises:       make([]*cAPI.Promise, 0),
63
			receivedPromises:   make([]*cAPI.Promise, 0),
64
			sentSignatures:     make([]*cAPI.Signature, 0),
65
			receivedSignatures: make([]*cAPI.Signature, 0),
66
		},
67
	}
Caro Axel's avatar
Caro Axel committed
68
	var err error
69
	_, _, _, err = m.auth.LoadFiles()
70 71 72 73
	if err != nil {
		return nil, err
	}

74
	m.mail = m.auth.Cert.Subject.CommonName
75

Caro Axel's avatar
Caro Axel committed
76
	m.cServer = m.GetServer()
77
	go func() { log.Fatalln(net.Listen("0.0.0.0:"+strconv.Itoa(port), m.cServer)) }()
Caro Axel's avatar
Caro Axel committed
78

79
	conn, err := net.Connect(m.auth.AddrPort, m.auth.Cert, m.auth.Key, m.auth.CA)
80 81 82 83
	if err != nil {
		return nil, err
	}

Caro Axel's avatar
Caro Axel committed
84
	m.platform = pAPI.NewPlatformClient(conn)
85
	m.platformConn = conn
86

87
	m.peersConn = make(map[string]*grpc.ClientConn)
Caro Axel's avatar
Caro Axel committed
88
	m.peers = make(map[string]*cAPI.ClientClient)
89
	for _, u := range c.Signers {
90
		if u.Email != m.auth.Cert.Subject.CommonName {
91 92
			m.peers[u.Email] = nil
		}
93 94 95 96 97 98 99
	}

	return m, nil
}

// ConnectToPeers tries to fetch the list of users for this contract, and tries to establish a connection to each peer.
func (m *SignatureManager) ConnectToPeers() error {
Caro Axel's avatar
Caro Axel committed
100
	stream, err := m.platform.JoinSignature(context.Background(), &pAPI.JoinSignatureRequest{
101 102 103 104 105 106 107 108 109 110 111 112 113
		ContractUuid: m.contract.UUID,
		Port:         uint32(m.localPort),
	})
	if err != nil {
		return err
	}

	for {
		userConnected, err := stream.Recv()
		if err != nil {
			return err
		}
		errorCode := userConnected.GetErrorCode()
Caro Axel's avatar
Caro Axel committed
114
		if errorCode.Code != pAPI.ErrorCode_SUCCESS {
115 116 117 118
			return errors.New(errorCode.Message)
		}
		ready, err := m.addPeer(userConnected.User)
		if err != nil {
119
			continue // Unable to connect to this user, ignore it for the moment
120 121 122 123 124 125 126 127 128 129
		}
		if ready {
			break
		}
	}

	return nil
}

// addPeer stores a peer from the platform and tries to establish a connection to this peer.
Caro Axel's avatar
Caro Axel committed
130
func (m *SignatureManager) addPeer(user *pAPI.User) (ready bool, err error) {
131 132 133 134
	if user == nil {
		err = errors.New("unexpected user format")
		return
	}
135 136 137
	if _, ok := m.peers[user.Email]; !ok {
		return // Ignore if unknown
	}
138

Caro Axel's avatar
Caro Axel committed
139
	addrPort := user.Ip + ":" + strconv.Itoa(int(user.Port))
140
	fmt.Println("- Trying to connect with", user.Email, "/", addrPort)
141

142
	conn, err := net.Connect(addrPort, m.auth.Cert, m.auth.Key, m.auth.CA)
Caro Axel's avatar
Caro Axel committed
143 144 145 146 147 148
	if err != nil {
		return false, err
	}

	// Sending Hello message
	client := cAPI.NewClientClient(conn)
149
	lastConnection := m.peers[user.Email]
Caro Axel's avatar
Caro Axel committed
150
	m.peers[user.Email] = &client
151 152 153
	// The connection is encapsulated into the interface, so we
	// need to create another way to access it
	m.peersConn[user.Email] = conn
154 155 156 157

	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()
	msg, err := client.Discover(ctx, &cAPI.Hello{Version: dfss.Version})
Caro Axel's avatar
Caro Axel committed
158 159 160
	if err != nil {
		return false, err
	}
161

Caro Axel's avatar
Caro Axel committed
162
	// Printing answer: application version
163 164
	// TODO check certificate
	fmt.Println("  Successfully connected!", "[", msg.Version, "]")
165 166

	// Check if we have any other peer to connect to
167 168 169 170
	if lastConnection == nil {
		m.nbReady++
		if m.nbReady == len(m.contract.Signers)-1 {
			return true, nil
171 172 173
		}
	}

174
	return false, nil
175
}
Loïck Bonniot's avatar
Loïck Bonniot committed
176 177 178

// SendReadySign sends the READY signal to the platform, and wait (potentially a long time) for START signal.
func (m *SignatureManager) SendReadySign() (signatureUUID string, err error) {
179
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
Loïck Bonniot's avatar
Loïck Bonniot committed
180
	defer cancel()
Loïck Bonniot's avatar
Loïck Bonniot committed
181
	launch, err := m.platform.ReadySign(ctx, &pAPI.ReadySignRequest{
Loïck Bonniot's avatar
Loïck Bonniot committed
182 183 184 185 186 187 188
		ContractUuid: m.contract.UUID,
	})
	if err != nil {
		return
	}

	errorCode := launch.GetErrorCode()
Loïck Bonniot's avatar
Loïck Bonniot committed
189
	if errorCode.Code != pAPI.ErrorCode_SUCCESS {
Loïck Bonniot's avatar
Loïck Bonniot committed
190 191 192 193
		err = errors.New(errorCode.Code.String() + " " + errorCode.Message)
		return
	}

194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
	// Check signers from platform data
	if len(m.contract.Signers) != len(launch.KeyHash) {
		err = errors.New("Corrupted DFSS file: bad number of signers, unable to sign safely")
		return
	}

	for i, s := range m.contract.Signers {
		if s.Hash != fmt.Sprintf("%x", launch.KeyHash[i]) {
			err = errors.New("Corrupted DFSS file: signer " + s.Email + " has an invalid hash, unable to sign safely")
			return
		}
	}

	m.sequence = launch.Sequence
	m.uuid = launch.SignatureUuid
209
	m.keyHash = launch.KeyHash
210
	signatureUUID = m.uuid
Loïck Bonniot's avatar
Loïck Bonniot committed
211 212
	return
}
213

214
// Initialize computes the values needed for the start of the signing
215
func (m *SignatureManager) Initialize() (uint32, int, error) {
216 217
	myID, err := m.FindID()
	if err != nil {
218
		return 0, 0, err
219 220
	}

221
	m.currentIndex, err = common.FindNextIndex(m.sequence, myID, -1)
222
	if err != nil {
223
		return 0, 0, err
224 225
	}

226
	nextIndex, err := common.FindNextIndex(m.sequence, myID, m.currentIndex)
227
	if err != nil {
228
		return 0, 0, err
229 230
	}

231
	return myID, nextIndex, nil
232 233 234 235
}

// FindID finds the sequence id for the user's email and the contract to sign
func (m *SignatureManager) FindID() (uint32, error) {
236 237
	signers := m.contract.Signers
	for id, signer := range signers {
238
		if signer.Email == m.mail {
239 240 241 242 243
			return uint32(id), nil
		}
	}
	return 0, errors.New("Mail couldn't be found amongst signers")
}