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

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

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

// SignatureManager handles the signature of a contract.
type SignatureManager struct {
	auth      *security.AuthContainer
	localPort int
	contract  *contract.JSON
Axel's avatar
Axel committed
26
27
	platform  pAPI.PlatformClient
	peers     map[string]*cAPI.ClientClient
28
	nbReady   int
Axel's avatar
Axel committed
29
	cServer   *grpc.Server
30
31
	sequence  []uint32
	uuid      string
32
33
34
35
36
37
38
39
40
41
42
	keyHash   [][]byte
	mail      string
	archives  *Archives
}

// Archives stores the recieved and sent messages, as evidence if needed
type Archives struct {
	sentPromises       []*cAPI.Promise
	recievedPromises   []*cAPI.Promise
	sentSignatures     []*cAPI.Signature
	recievedSignatures []*cAPI.Signature
43
44
45
46
47
48
49
50
}

// 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,
51
52
53
54
55
56
		archives: &Archives{
			sentPromises:       make([]*cAPI.Promise, 0),
			recievedPromises:   make([]*cAPI.Promise, 0),
			sentSignatures:     make([]*cAPI.Signature, 0),
			recievedSignatures: make([]*cAPI.Signature, 0),
		},
57
	}
Axel's avatar
Axel committed
58
	var err error
59
	_, _, _, err = m.auth.LoadFiles()
60
61
62
63
	if err != nil {
		return nil, err
	}

64
	m.mail = m.auth.Cert.Subject.CommonName
65

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

69
	conn, err := net.Connect(m.auth.AddrPort, m.auth.Cert, m.auth.Key, m.auth.CA)
70
71
72
73
	if err != nil {
		return nil, err
	}

Axel's avatar
Axel committed
74
	m.platform = pAPI.NewPlatformClient(conn)
75

Axel's avatar
Axel committed
76
	m.peers = make(map[string]*cAPI.ClientClient)
77
	for _, u := range c.Signers {
78
		if u.Email != m.auth.Cert.Subject.CommonName {
79
80
			m.peers[u.Email] = nil
		}
81
82
83
84
85
86
87
	}

	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 {
Axel's avatar
Axel committed
88
	stream, err := m.platform.JoinSignature(context.Background(), &pAPI.JoinSignatureRequest{
89
90
91
92
93
94
95
96
97
98
99
100
101
		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()
Axel's avatar
Axel committed
102
		if errorCode.Code != pAPI.ErrorCode_SUCCESS {
103
104
105
106
			return errors.New(errorCode.Message)
		}
		ready, err := m.addPeer(userConnected.User)
		if err != nil {
107
			continue // Unable to connect to this user, ignore it for the moment
108
109
110
111
112
113
114
115
116
117
		}
		if ready {
			break
		}
	}

	return nil
}

// addPeer stores a peer from the platform and tries to establish a connection to this peer.
Axel's avatar
Axel committed
118
func (m *SignatureManager) addPeer(user *pAPI.User) (ready bool, err error) {
119
120
121
122
	if user == nil {
		err = errors.New("unexpected user format")
		return
	}
123
124
125
	if _, ok := m.peers[user.Email]; !ok {
		return // Ignore if unknown
	}
126

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

130
	conn, err := net.Connect(addrPort, m.auth.Cert, m.auth.Key, m.auth.CA)
Axel's avatar
Axel committed
131
132
133
134
135
136
	if err != nil {
		return false, err
	}

	// Sending Hello message
	client := cAPI.NewClientClient(conn)
137
	lastConnection := m.peers[user.Email]
Axel's avatar
Axel committed
138
	m.peers[user.Email] = &client
139
140
141
142

	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()
	msg, err := client.Discover(ctx, &cAPI.Hello{Version: dfss.Version})
Axel's avatar
Axel committed
143
144
145
	if err != nil {
		return false, err
	}
146

Axel's avatar
Axel committed
147
	// Printing answer: application version
148
149
	// TODO check certificate
	fmt.Println("  Successfully connected!", "[", msg.Version, "]")
150
151

	// Check if we have any other peer to connect to
152
153
154
155
	if lastConnection == nil {
		m.nbReady++
		if m.nbReady == len(m.contract.Signers)-1 {
			return true, nil
156
157
158
		}
	}

159
	return false, nil
160
}
Loïck Bonniot's avatar
Loïck Bonniot committed
161
162
163

// 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) {
164
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
Loïck Bonniot's avatar
Loïck Bonniot committed
165
	defer cancel()
Loïck Bonniot's avatar
Loïck Bonniot committed
166
	launch, err := m.platform.ReadySign(ctx, &pAPI.ReadySignRequest{
Loïck Bonniot's avatar
Loïck Bonniot committed
167
168
169
170
171
172
173
		ContractUuid: m.contract.UUID,
	})
	if err != nil {
		return
	}

	errorCode := launch.GetErrorCode()
Loïck Bonniot's avatar
Loïck Bonniot committed
174
	if errorCode.Code != pAPI.ErrorCode_SUCCESS {
Loïck Bonniot's avatar
Loïck Bonniot committed
175
176
177
178
		err = errors.New(errorCode.Code.String() + " " + errorCode.Message)
		return
	}

179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
	// 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
194
	m.keyHash = launch.KeyHash
195
	signatureUUID = m.uuid
Loïck Bonniot's avatar
Loïck Bonniot committed
196
197
	return
}
198

199
// Sign makes the SignatureManager perform its specified signature
200
func (m *SignatureManager) Sign() error {
201
202
	myID, currentIndex, nextIndex, err := m.Initialize()
	if err != nil {
203
204
205
		return err
	}

206
207
208
209
	// Promess rounds
	for nextIndex != -1 {
		pendingSet, err := common.GetPendingSet(m.sequence, myID, currentIndex)
		if err != nil {
210
211
212
			return err
		}

213
214
		sendSet, err := common.GetSendSet(m.sequence, myID, currentIndex)
		if err != nil {
215
216
217
218
			return err
		}

		// Reception of the due promesses
219
		for len(pendingSet) != 0 {
220
221
222
223
224
225
226
227
228
229
			i := 0
			// TODO
			// Improve, because potential memory leak
			// see https://github.com/golang/go/wiki/SliceTricks
			pendingSet = append(pendingSet[:i], pendingSet[i+1:]...)
		}

		c := make(chan int)
		// Sending of the due promesses
		/*
230
231
232
233
234
235
236
			for _, id := range sendSet {
				go func(id) {
					promise, err := m.CreatePromise(id)
					recpt := m.SendPromise(promise, id)
					c <- id
				}(id)
			}
237
		*/
238

239
240
		// Verifying we sent all the due promesses
		for _ = range sendSet {
241
242
243
244
245
246
247
			<-c
		}

		currentIndex = nextIndex
		nextIndex, err = common.FindNextIndex(m.sequence, myID, currentIndex)
		if err != nil {
			return err
248
249
250
251
		}
	}

	// Signature round
252
253
254
255
256
257
258
259
	err = m.SendAllSigns()
	if err != nil {
		return err
	}
	err = m.RecieveAllSigns()
	if err != nil {
		return err
	}
260
261
262
263

	return nil
}

264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
// Initialize computes the values needed for the start of the signing
func (m *SignatureManager) Initialize() (uint32, int, int, error) {
	myID, err := m.FindID()
	if err != nil {
		return 0, 0, 0, err
	}

	currentIndex, err := common.FindNextIndex(m.sequence, myID, -1)
	if err != nil {
		return 0, 0, 0, err
	}

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

	return myID, currentIndex, nextIndex, nil
}

// FindID finds the sequence id for the user's email and the contract to sign
func (m *SignatureManager) FindID() (uint32, error) {
286
287
	signers := m.contract.Signers
	for id, signer := range signers {
288
		if signer.Email == m.mail {
289
290
291
292
293
294
			return uint32(id), nil
		}
	}
	return 0, errors.New("Mail couldn't be found amongst signers")
}

295
296
297
298
299
300
301
302
303
304
305
306
307
// CreatePromise creates a promise from 'from' to 'to', in the context of the SignatureManager
// provided the specified sequence indexes are valid
func (m *SignatureManager) CreatePromise(from, to uint32) (*cAPI.Promise, error) {
	if int(from) >= len(m.keyHash) || int(to) >= len(m.keyHash) {
		return &cAPI.Promise{}, errors.New("Invalid id for promise creation")
	}
	promise := &cAPI.Promise{
		RecipientKeyHash: m.keyHash[to],
		SenderKeyHash:    m.keyHash[from],
		SignatureUuid:    m.uuid,
		ContractUuid:     m.contract.UUID,
	}
	return promise, nil
308
309
}

310
// SendPromise sends the specified promise to the specified peer
311
// TODO
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
func (m *SignatureManager) SendPromise(promise *cAPI.Promise, to uint32) (*pAPI.ErrorCode, error) {
	connection, err := m.GetClient(to)
	if err != nil {
		return &pAPI.ErrorCode{}, err
	}

	// TODO
	// Handle the timeout
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
	defer cancel()

	errCode, err := (*connection).TreatPromise(ctx, promise)
	if err != nil {
		return &pAPI.ErrorCode{}, err
	}

	m.archives.sentPromises = append(m.archives.sentPromises, promise)

	return errCode, nil
331
332
}

333
334
335
336
337
338
339
340
341
342
343
344
// GetClient retrieves the Client to the specified sequence id provided it exists
func (m *SignatureManager) GetClient(to uint32) (*cAPI.ClientClient, error) {
	mailto := m.contract.Signers[to].Email

	if _, ok := m.peers[mailto]; !ok {
		return nil, fmt.Errorf("No connection to user %s", mailto)
	}

	return m.peers[mailto], nil
}

// SendAllSigns creates and sends signatures to all the signers of the contract
345
// TODO
346
// Use goroutines to send in parallel
347
func (m *SignatureManager) SendAllSigns() error {
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
	myID, err := m.FindID()
	if err != nil {
		return err
	}

	sendSet := common.GetAllButOne(m.sequence, myID)

	for _, id := range sendSet {
		signature, err := m.CreateSignature(myID, id)
		if err != nil {
			return err
		}

		_, err = m.SendSignature(signature, id)
		if err != nil {
			return err
		}
	}

367
368
369
	return nil
}

370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
// CreateSignature creates a signature from from to to, in the context of the SignatureManager
// provided the specified sequence indexes are valid
// TODO
// Implement a true cryptographic signature
func (m *SignatureManager) CreateSignature(from, to uint32) (*cAPI.Signature, error) {
	if int(from) >= len(m.keyHash) || int(to) >= len(m.keyHash) {
		return &cAPI.Signature{}, errors.New("Invalid id for signature creation")
	}
	signature := &cAPI.Signature{
		RecipientKeyHash: m.keyHash[to],
		SenderKeyHash:    m.keyHash[from],
		Signature:        "Signature",
		SignatureUuid:    m.uuid,
		ContractUuid:     m.contract.UUID,
	}
	return signature, nil
}

// SendSignature sends the specified signature to the specified peer
// TODO
func (m *SignatureManager) SendSignature(signature *cAPI.Signature, to uint32) (*pAPI.ErrorCode, error) {
	connection, err := m.GetClient(to)
	if err != nil {
		return &pAPI.ErrorCode{}, err
	}

	// TODO
	// Handle the timeout
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
	defer cancel()

	errCode, err := (*connection).TreatSignature(ctx, signature)
	if err != nil {
		return &pAPI.ErrorCode{}, err
	}

	m.archives.sentSignatures = append(m.archives.sentSignatures, signature)

	return errCode, nil
}

// RecieveAllSigns is not done yet
412
413
414
// TODO
func (m *SignatureManager) RecieveAllSigns() error {
	return nil
415
}