server.go 9.65 KB
Newer Older
1
// Package server provides the ttp server.
2
3
4
package server

import (
Axel's avatar
Axel committed
5
	"errors"
6
7
	"fmt"
	"os"
Axel's avatar
Axel committed
8
	"sync"
9

Axel's avatar
Axel committed
10
	cAPI "dfss/dfssc/api"
11
	"dfss/dfssc/security"
12
	dAPI "dfss/dfssd/api"
Axel's avatar
Axel committed
13
14
15
	tAPI "dfss/dfsst/api"
	"dfss/dfsst/entities"
	"dfss/dfsst/resolve"
16
17
	"dfss/mgdb"
	"dfss/net"
ElyKar's avatar
ElyKar committed
18
	"github.com/spf13/viper"
19
20
	"golang.org/x/net/context"
	"google.golang.org/grpc"
Axel's avatar
Axel committed
21
	"gopkg.in/mgo.v2/bson"
22
23
)

Axel's avatar
Axel committed
24
25
26
// InternalError : constant string used to return a generic error message through gRPC in case of an internal error.
const InternalError string = "Internal server error"

27
type ttpServer struct {
Axel's avatar
Axel committed
28
29
30
	DB        *mgdb.MongoManager
	globalMut *sync.Mutex
	mutMap    map[bson.ObjectId]*sync.Mutex
31
32
}

Axel's avatar
Axel committed
33
34
// Alert route for the TTP.
func (server *ttpServer) Alert(ctx context.Context, in *tAPI.AlertRequest) (*tAPI.TTPResponse, error) {
Axel's avatar
Axel committed
35
36
	valid, signatureUUID, signers, senderIndex := entities.IsRequestValid(ctx, in.Promises)
	if !valid {
37
		dAPI.DLog("invalid request from " + net.GetCN(&ctx))
Axel's avatar
Axel committed
38
39
		return nil, errors.New(InternalError)
	}
Axel's avatar
Axel committed
40

Axel's avatar
Axel committed
41
42
43
44
45
46
47
48
49
50
51
	server.globalMut.Lock()

	_, ok := server.mutMap[signatureUUID]
	if !ok {
		server.mutMap[signatureUUID] = &sync.Mutex{}
	}
	server.mutMap[signatureUUID].Lock()

	server.globalMut.Unlock()
	defer server.mutMap[signatureUUID].Unlock()

Axel's avatar
Axel committed
52
53
	dAPI.DLog("resolve index is: " + fmt.Sprint(in.Index))
	valid = int(in.Index) < len(in.Promises[0].Context.Sequence)
Axel's avatar
Axel committed
54
	if !valid {
55
		dAPI.DLog("invalid sequence index from " + net.GetCN(&ctx))
Axel's avatar
Axel committed
56
57
58
59
60
61
		return nil, errors.New(InternalError)
	}
	// Now we know that the request contains information correctly signed by the platform,
	// with the same signatureUUID (thus signed information) for all promises, and sent by a valid signer
	// wrt to the signed signers' hashes

Axel's avatar
Axel committed
62
63
	dAPI.DLog("Resolve request from " + net.GetCN(&ctx) + " is valid")

64
	manager := entities.NewArchivesManager(server.DB)
Axel's avatar
Axel committed
65
66
67
68
69
	err := manager.InitializeArchives(in.Promises[0], signatureUUID, &signers)
	if err != nil {
		dAPI.DLog("error occured during the initialization of the signature archives")
		return nil, err
	}
Axel's avatar
Axel committed
70
71
72
73
74
	// Now archives contains the new or already present SignatureArchives

	// We check if we have already sent an abort token to the sender of the request
	stop, message, err := server.handleAbortedSender(manager, senderIndex)
	if stop {
75
		dAPI.DLog("already sent an abort token to " + net.GetCN(&ctx))
Axel's avatar
Axel committed
76
77
78
79
		return message, err
	}

	// We check that the sender of the request sent valid and complete information
Axel's avatar
Axel committed
80
	stop, message, tmpPromises, err := server.handleInvalidPromises(manager, in.Promises, senderIndex, in.Index)
Axel's avatar
Axel committed
81
	if stop {
82
		dAPI.DLog("sent abort token to " + net.GetCN(&ctx))
Axel's avatar
Axel committed
83
84
85
86
87
88
89
		return message, err
	}
	// Now we are sure that the sender of the AlertRequest is not dishonest

	// We try to use the already generated contract if it exists
	generated, contract := manager.WasContractSigned()
	if generated {
90
		dAPI.DLog("sent signed contract to " + net.GetCN(&ctx))
Axel's avatar
Axel committed
91
92
93
94
95
96
97
98
99
100
101
102
		return &tAPI.TTPResponse{
			Abort:    false,
			Contract: contract,
		}, nil
	}

	// If we didn't already generate the signed contract, we take into account the new promises
	// Computing the dishonest signers wrt to the new evidence
	server.updateArchiveWithEvidence(manager, tmpPromises)
	// Try to generate the contract now
	message, err = server.handleContractGenerationTry(manager)
	// We manually update the database
Axel's avatar
Axel committed
103
	ok, err = server.DB.Get("signatures").UpdateByID(*(manager.Archives))
Axel's avatar
Axel committed
104
	if !ok {
105
		fmt.Fprintln(os.Stderr, err)
Axel's avatar
Axel committed
106
107
108
		return nil, errors.New(InternalError)
	}

109
110
111
112
113
114
	if message.Abort {
		dAPI.DLog("sent abort token to " + net.GetCN(&ctx))
	} else {
		dAPI.DLog("sent signed contract to " + net.GetCN(&ctx))
	}

Axel's avatar
Axel committed
115
116
117
118
119
120
121
122
	return message, err
}

// handleAbortedSender : if the specified signer has already recieved an abort token, adds him to the dishonest signers of the specified
// signatureArchives, and returns a boolean that states if we should stop the execution of the resolve protocol, and the response that should be sent back to him.
//
// Updates the database with the new aborted signers.
// If an error occurs during this process, it is returned.
123
func (server *ttpServer) handleAbortedSender(manager *entities.ArchivesManager, senderIndex uint32) (bool, *tAPI.TTPResponse, error) {
Axel's avatar
Axel committed
124
	if manager.HasReceivedAbortToken(senderIndex) {
Axel's avatar
Axel committed
125
		dAPI.DLog("Sender has already contacted the ttp. He is dishonnest.")
Axel's avatar
Axel committed
126
127
		manager.AddToDishonest(senderIndex)

Axel's avatar
Axel committed
128
		ok, err := manager.DB.Get("signatures").UpdateByID(*(manager.Archives))
Axel's avatar
Axel committed
129
		if !ok {
130
			fmt.Fprintln(os.Stderr, err)
Axel's avatar
Axel committed
131
132
133
134
135
136
137
138
			return true, nil, errors.New(InternalError)
		}

		return true, &tAPI.TTPResponse{
			Abort:    true,
			Contract: nil,
		}, nil
	}
Axel's avatar
Axel committed
139
	dAPI.DLog("sender has never contacted the ttp before")
Axel's avatar
Axel committed
140
141
142
143
144
145
146
147
148
149
150
151
152
153
	return false, nil, nil
}

// handleInvalidPromises : if the specified signer has sent us a valid request, but with invalid promises, ie:
// - he sent us impossible information wrt the information signed by the platform
// OR
// - he sent not enough information wrt the information signed by the platform and the signing protocol
// then he is added to the dishonest signers.
//
// Returns a boolean that states if we should stop the execution of the resolve protocol, and the response that should be sent back to him.
// If the promises are valid, return them in the simplified form of an array of *entities.Promise
//
// Updates the database with the new aborted signer.
// If an error occurs during this process, it is returned.
Axel's avatar
Axel committed
154
func (server *ttpServer) handleInvalidPromises(manager *entities.ArchivesManager, promises []*cAPI.Promise, senderIndex, stepIndex uint32) (bool, *tAPI.TTPResponse, []*entities.Promise, error) {
155
	valid, tmpPromises := entities.ArePromisesValid(promises)
Axel's avatar
Axel committed
156
157
158
	if valid {
		dAPI.DLog("received promises are valid")
	}
Axel's avatar
Axel committed
159
	complete := resolve.ArePromisesComplete(tmpPromises, promises[0], stepIndex)
Axel's avatar
Axel committed
160
161
162
	if complete {
		dAPI.DLog("received promises are complete")
	}
Axel's avatar
Axel committed
163
	if !valid || !complete {
Axel's avatar
Axel committed
164
165
166
167
168
169
		if !valid {
			dAPI.DLog("received promises are not valid")
		}
		if !complete {
			dAPI.DLog("received promises are not complete")
		}
Axel's avatar
Axel committed
170
171
172
		manager.AddToAbort(senderIndex)
		manager.AddToDishonest(senderIndex)

Axel's avatar
Axel committed
173
		ok, err := manager.DB.Get("signatures").UpdateByID(*(manager.Archives))
Axel's avatar
Axel committed
174
		if !ok {
175
			fmt.Fprintln(os.Stderr, err)
Axel's avatar
Axel committed
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
			return true, nil, nil, errors.New(InternalError)
		}

		return true, &tAPI.TTPResponse{
			Abort:    true,
			Contract: nil,
		}, nil, nil
	}

	return false, nil, tmpPromises, nil
}

// updateArchiveWithEvidence : computes the dishonest signers from the new provided evidence, and updates the specified signatureArchives accordingly.
//
// DOES NOT UPDATE THE DATABASE (should be handled manually)
191
func (server *ttpServer) updateArchiveWithEvidence(manager *entities.ArchivesManager, tmpPromises []*entities.Promise) {
Axel's avatar
Axel committed
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
	computedDishonest := resolve.ComputeDishonestSigners(manager.Archives, tmpPromises)

	for _, di := range computedDishonest {
		manager.AddToDishonest(di)
	}

	for _, p := range tmpPromises {
		manager.AddPromise(p)
	}
}

// handleContractGenerationTry : tries to generate the signed contract from the specified signatureArchives.
// Returns the response to send back to the sender of the request.
//
// Does not take into account if the sender is dishonest.
// If the contract has been successfully generated, returns it. Otherwise, returns an abort token.
//
// DOES NOT UPDATE THE DATABASE (should be handled manually)
210
func (server *ttpServer) handleContractGenerationTry(manager *entities.ArchivesManager) (*tAPI.TTPResponse, error) {
Axel's avatar
Axel committed
211
212
213
214
215
216
217
218
219
220
221
222
223
224
	generated, contract := resolve.Solve(manager)
	if !generated {
		return &tAPI.TTPResponse{
			Abort:    true,
			Contract: nil,
		}, nil
	}

	// We add the generated contract to the signatureArchives
	manager.Archives.SignedContract = contract
	return &tAPI.TTPResponse{
		Abort:    false,
		Contract: contract,
	}, nil
225
226
}

Axel's avatar
Axel committed
227
228
// Recover route for the TTP.
func (server *ttpServer) Recover(ctx context.Context, in *tAPI.RecoverRequest) (*tAPI.TTPResponse, error) {
Axel's avatar
Axel committed
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
	if !bson.IsObjectIdHex(in.SignatureUUID) {
		return nil, errors.New("Invalid signature uuid.")
	}
	bsonUUID := bson.ObjectIdHex(in.SignatureUUID)

	manager := entities.NewArchivesManager(server.DB)
	present, archives := manager.ContainsSignature(bsonUUID)
	if !present {
		return nil, errors.New("Unknown signature uuid.")
	}
	manager.Archives = archives

	contract, err := handleRecover(ctx, manager)
	if err != nil {
		return nil, err
	}

	return &tAPI.TTPResponse{Contract: contract}, nil
}

func handleRecover(ctx context.Context, manager *entities.ArchivesManager) ([]byte, error) {
	senderHash := net.GetClientHash(&ctx)
	if senderHash == nil {
		return []byte{}, errors.New("Bad authentication.")
	}

	present, senderID := manager.Archives.ContainsSigner(senderHash)
	if !present {
		return []byte{}, errors.New("Signer was not part of the signature.")
	}

	aborted := manager.HasReceivedAbortToken(senderID)
	if aborted {
		return []byte{}, errors.New("Signer was aborted.")
	}

	_, contract := manager.WasContractSigned()
	return contract, nil
267
268
}

Axel's avatar
Axel committed
269
// GetServer returns the gRPC server.
ElyKar's avatar
ElyKar committed
270
271
func GetServer() *grpc.Server {
	// We can do that because NewAuthContainer is looking for "file_ca", "file_cert", and "file_key" in viper, which are set by the TTP
272
273
	entities.AuthContainer = security.NewAuthContainer(viper.GetString("password"))
	ca, cert, key, err := entities.AuthContainer.LoadFiles()
274
	if err != nil {
275
		fmt.Fprintln(os.Stderr, "An error occured during the private key and certificates retrieval:", err)
276
277
278
		os.Exit(1)
	}

ElyKar's avatar
ElyKar committed
279
	dbManager, err := mgdb.NewManager(viper.GetString("dbURI"))
280
281
	if err != nil {
		fmt.Fprintln(os.Stderr, "An error occured during the connection to MongoDB:", err)
282
		os.Exit(1)
283
284
	}

Axel's avatar
Axel committed
285
286
	mutmap := make(map[bson.ObjectId]*sync.Mutex)

287
	server := &ttpServer{
Axel's avatar
Axel committed
288
289
290
		DB:        dbManager,
		globalMut: &sync.Mutex{},
		mutMap:    mutmap,
291
	}
Axel's avatar
Axel committed
292

293
	netServer := net.NewServer(cert, key, ca)
Axel's avatar
Axel committed
294
	tAPI.RegisterTTPServer(netServer, server)
295
296
	return netServer
}