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

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

Caro Axel's avatar
Caro Axel committed
10
	cAPI "dfss/dfssc/api"
11
	"dfss/dfssc/security"
12
	dAPI "dfss/dfssd/api"
Caro Axel's avatar
Caro 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"
Caro Axel's avatar
Caro Axel committed
21
	"gopkg.in/mgo.v2/bson"
22
23
)

Caro Axel's avatar
Caro 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 {
Caro Axel's avatar
Caro Axel committed
28
29
30
	DB        *mgdb.MongoManager
	globalMut *sync.Mutex
	mutMap    map[bson.ObjectId]*sync.Mutex
31
32
}

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

Caro Axel's avatar
Caro 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()

Caro Axel's avatar
Caro Axel committed
52
53
	dAPI.DLog("resolve index is: " + fmt.Sprint(in.Index))
	valid = int(in.Index) < len(in.Promises[0].Context.Sequence)
Caro Axel's avatar
Caro Axel committed
54
	if !valid {
55
		dAPI.DLog("invalid sequence index from " + net.GetCN(&ctx))
Caro Axel's avatar
Caro 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

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

64
	manager := entities.NewArchivesManager(server.DB)
Caro Axel's avatar
Caro 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
	}
Caro Axel's avatar
Caro 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))
Caro Axel's avatar
Caro Axel committed
76
77
78
79
		return message, err
	}

	// We check that the sender of the request sent valid and complete information
Caro Axel's avatar
Caro Axel committed
80
	stop, message, tmpPromises, err := server.handleInvalidPromises(manager, in.Promises, senderIndex, in.Index)
Caro Axel's avatar
Caro Axel committed
81
	if stop {
82
		dAPI.DLog("sent abort token to " + net.GetCN(&ctx))
Caro Axel's avatar
Caro 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))
Caro Axel's avatar
Caro 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
Caro Axel's avatar
Caro Axel committed
103
	ok, err = server.DB.Get("signatures").UpdateByID(*(manager.Archives))
Caro Axel's avatar
Caro Axel committed
104
	if !ok {
105
		fmt.Fprintln(os.Stderr, err)
Caro Axel's avatar
Caro 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))
	}

Caro Axel's avatar
Caro 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) {
Caro Axel's avatar
Caro Axel committed
124
	if manager.HasReceivedAbortToken(senderIndex) {
Caro Axel's avatar
Caro Axel committed
125
		dAPI.DLog("Sender has already contacted the ttp. He is dishonnest.")
Caro Axel's avatar
Caro Axel committed
126
127
		manager.AddToDishonest(senderIndex)

Caro Axel's avatar
Caro Axel committed
128
		ok, err := manager.DB.Get("signatures").UpdateByID(*(manager.Archives))
Caro Axel's avatar
Caro Axel committed
129
		if !ok {
130
			fmt.Fprintln(os.Stderr, err)
Caro Axel's avatar
Caro 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
	}
Caro Axel's avatar
Caro Axel committed
139
	dAPI.DLog("sender has never contacted the ttp before")
Caro Axel's avatar
Caro 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.
Caro Axel's avatar
Caro 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)
Caro Axel's avatar
Caro Axel committed
156
157
158
	if valid {
		dAPI.DLog("received promises are valid")
	}
Caro Axel's avatar
Caro Axel committed
159
	complete := resolve.ArePromisesComplete(tmpPromises, promises[0], stepIndex)
Caro Axel's avatar
Caro Axel committed
160
161
162
	if complete {
		dAPI.DLog("received promises are complete")
	}
Caro Axel's avatar
Caro Axel committed
163
	if !valid || !complete {
Caro Axel's avatar
Caro 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")
		}
Caro Axel's avatar
Caro Axel committed
170
171
172
		manager.AddToAbort(senderIndex)
		manager.AddToDishonest(senderIndex)

Caro Axel's avatar
Caro Axel committed
173
		ok, err := manager.DB.Get("signatures").UpdateByID(*(manager.Archives))
Caro Axel's avatar
Caro Axel committed
174
		if !ok {
175
			fmt.Fprintln(os.Stderr, err)
Caro Axel's avatar
Caro 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) {
Caro Axel's avatar
Caro 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) {
Caro Axel's avatar
Caro 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
}

Caro Axel's avatar
Caro Axel committed
227
228
229
// Recover route for the TTP.
func (server *ttpServer) Recover(ctx context.Context, in *tAPI.RecoverRequest) (*tAPI.TTPResponse, error) {
	// TODO
230
231
232
	return nil, nil
}

Caro Axel's avatar
Caro Axel committed
233
// GetServer returns the gRPC server.
ElyKar's avatar
ElyKar committed
234
235
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
236
237
	entities.AuthContainer = security.NewAuthContainer(viper.GetString("password"))
	ca, cert, key, err := entities.AuthContainer.LoadFiles()
238
	if err != nil {
239
		fmt.Fprintln(os.Stderr, "An error occured during the private key and certificates retrieval:", err)
240
241
242
		os.Exit(1)
	}

ElyKar's avatar
ElyKar committed
243
	dbManager, err := mgdb.NewManager(viper.GetString("dbURI"))
244
245
	if err != nil {
		fmt.Fprintln(os.Stderr, "An error occured during the connection to MongoDB:", err)
246
		os.Exit(1)
247
248
	}

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

251
	server := &ttpServer{
Caro Axel's avatar
Caro Axel committed
252
253
254
		DB:        dbManager,
		globalMut: &sync.Mutex{},
		mutMap:    mutmap,
255
	}
Caro Axel's avatar
Caro Axel committed
256

257
	netServer := net.NewServer(cert, key, ca)
Caro Axel's avatar
Caro Axel committed
258
	tAPI.RegisterTTPServer(netServer, server)
259
260
	return netServer
}