starter.go 6.35 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

Caro Axel's avatar
Caro Axel committed
10
11
	"dfss"
	cAPI "dfss/dfssc/api"
12
	"dfss/dfssc/common"
13
	"dfss/dfssc/security"
Caro Axel's avatar
Caro Axel committed
14
	pAPI "dfss/dfssp/api"
15
16
17
	"dfss/dfssp/contract"
	"dfss/net"
	"golang.org/x/net/context"
Caro Axel's avatar
Caro 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
Caro Axel's avatar
Caro Axel committed
26
27
	platform  pAPI.PlatformClient
	peers     map[string]*cAPI.ClientClient
28
	nbReady   int
Caro Axel's avatar
Caro Axel committed
29
	cServer   *grpc.Server
30
31
	sequence  []uint32
	uuid      string
32
	mail	  string
33
34
35
36
37
38
39
40
41
}

// 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,
	}
Caro Axel's avatar
Caro Axel committed
42
	var err error
43
	_, x509cert, _, err := m.auth.LoadFiles()
44
45
46
47
	if err != nil {
		return nil, err
	}

48
49
	m.mail = x509cert.Subject.CommonName

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

53
	conn, err := net.Connect(m.auth.AddrPort, m.auth.Cert, m.auth.Key, m.auth.CA)
54
55
56
57
	if err != nil {
		return nil, err
	}

Caro Axel's avatar
Caro Axel committed
58
	m.platform = pAPI.NewPlatformClient(conn)
59

Caro Axel's avatar
Caro Axel committed
60
	m.peers = make(map[string]*cAPI.ClientClient)
61
	for _, u := range c.Signers {
62
		if u.Email != m.auth.Cert.Subject.CommonName {
63
64
			m.peers[u.Email] = nil
		}
65
66
67
68
69
70
71
	}

	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
72
	stream, err := m.platform.JoinSignature(context.Background(), &pAPI.JoinSignatureRequest{
73
74
75
76
77
78
79
80
81
82
83
84
85
		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
86
		if errorCode.Code != pAPI.ErrorCode_SUCCESS {
87
88
89
90
			return errors.New(errorCode.Message)
		}
		ready, err := m.addPeer(userConnected.User)
		if err != nil {
91
			continue // Unable to connect to this user, ignore it for the moment
92
93
94
95
96
97
98
99
100
101
		}
		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
102
func (m *SignatureManager) addPeer(user *pAPI.User) (ready bool, err error) {
103
104
105
106
	if user == nil {
		err = errors.New("unexpected user format")
		return
	}
107
108
109
	if _, ok := m.peers[user.Email]; !ok {
		return // Ignore if unknown
	}
110

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

114
	conn, err := net.Connect(addrPort, m.auth.Cert, m.auth.Key, m.auth.CA)
Caro Axel's avatar
Caro Axel committed
115
116
117
118
119
120
	if err != nil {
		return false, err
	}

	// Sending Hello message
	client := cAPI.NewClientClient(conn)
121
	lastConnection := m.peers[user.Email]
Caro Axel's avatar
Caro Axel committed
122
	m.peers[user.Email] = &client
123
124
125
126

	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
127
128
129
	if err != nil {
		return false, err
	}
130

Caro Axel's avatar
Caro Axel committed
131
	// Printing answer: application version
132
133
	// TODO check certificate
	fmt.Println("  Successfully connected!", "[", msg.Version, "]")
134
135

	// Check if we have any other peer to connect to
136
137
138
139
	if lastConnection == nil {
		m.nbReady++
		if m.nbReady == len(m.contract.Signers)-1 {
			return true, nil
140
141
142
		}
	}

143
	return false, nil
144
}
Loïck Bonniot's avatar
Loïck Bonniot committed
145
146
147

// 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) {
148
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
Loïck Bonniot's avatar
Loïck Bonniot committed
149
	defer cancel()
Loïck Bonniot's avatar
Loïck Bonniot committed
150
	launch, err := m.platform.ReadySign(ctx, &pAPI.ReadySignRequest{
Loïck Bonniot's avatar
Loïck Bonniot committed
151
152
153
154
155
156
157
		ContractUuid: m.contract.UUID,
	})
	if err != nil {
		return
	}

	errorCode := launch.GetErrorCode()
Loïck Bonniot's avatar
Loïck Bonniot committed
158
	if errorCode.Code != pAPI.ErrorCode_SUCCESS {
Loïck Bonniot's avatar
Loïck Bonniot committed
159
160
161
162
		err = errors.New(errorCode.Code.String() + " " + errorCode.Message)
		return
	}

163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
	// 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
	signatureUUID = m.uuid
Loïck Bonniot's avatar
Loïck Bonniot committed
179
180
	return
}
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271

// Sign make the SignatureManager perform its specified signature
func (m *SignatureManager) Sign() error {
	mySeqId, err := m.FindId()
	if (err != nil) {
		return err
	}

	curIndex, err := common.FindNextIndex(m.sequence, mySeqId, -1)
	if (err != nil) {
		return err
	}

	nextIndex, err := common.FindNextIndex(m.sequence, mySeqId, curIndex)
	
	// Promess tounds
	for (nextIndex != -1) {
		pendingSet, err := common.GetPendingSet(m.sequence, mySeqId, curIndex)
		if (err != nil) {
			return err
		}

		sendSet, err := common.GetSendSet(m.sequence, mySeqId, curIndex)
		if (err != nil) {
			return err
		}

		// Reception of the due promesses
		for (len(pendingSet) != 0) {
			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
		/*
		for _, id := range sendSet {
			go func(id) {
				promise := m.CreatePromise(id)
				recpt := m.SendPromise(promise, id)
				c <- id
			}(id)
		}
		*/
		
		// Verifying we sent all the due promesses
		for _ = range sendSet {
			<- c
		}
	}

	// Signature round
	m.SendAllSigns()
	m.RecieveAllSigns()

	return nil
}

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

// TODO
func (m *SignatureManager) CreatePromise(id uint32) error {
	return nil
}

// TODO
func (m *SignatureManager) SendPromise(id uint32) error {
	return nil
}

// TODO
func (m *SignatureManager) SendAllSigns() error {
	return nil
}

// TODO
func (m *SignatureManager) RecieveAllSigns() error {
	return nil
}