server.go 8.77 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 {
Caro Axel's avatar
Caro Axel committed
82
		dAPI.DLog("invalid promise caused stop")
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 {
Caro Axel's avatar
Caro Axel committed
90
		dAPI.DLog("sending the signed contract")
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 {
Caro Axel's avatar
Caro Axel committed
105
		dAPI.DLog("error during 'UpdateByID' l.81" + fmt.Sprint(err.Error()))
Caro Axel's avatar
Caro Axel committed
106 107 108 109 110 111 112 113 114 115 116
		return nil, errors.New(InternalError)
	}

	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.
117
func (server *ttpServer) handleAbortedSender(manager *entities.ArchivesManager, senderIndex uint32) (bool, *tAPI.TTPResponse, error) {
Caro Axel's avatar
Caro Axel committed
118
	if manager.HasReceivedAbortToken(senderIndex) {
Caro Axel's avatar
Caro Axel committed
119
		dAPI.DLog("Sender has already contacted the ttp. He is dishonnest.")
Caro Axel's avatar
Caro Axel committed
120 121
		manager.AddToDishonest(senderIndex)

Caro Axel's avatar
Caro Axel committed
122
		ok, err := manager.DB.Get("signatures").UpdateByID(*(manager.Archives))
Caro Axel's avatar
Caro Axel committed
123
		if !ok {
Caro Axel's avatar
Caro Axel committed
124
			dAPI.DLog("error during 'UpdateByID' l.99" + fmt.Sprint(err.Error()))
Caro Axel's avatar
Caro Axel committed
125 126 127 128 129 130 131 132
			return true, nil, errors.New(InternalError)
		}

		return true, &tAPI.TTPResponse{
			Abort:    true,
			Contract: nil,
		}, nil
	}
Caro Axel's avatar
Caro Axel committed
133
	dAPI.DLog("sender has never contacted the ttp before")
Caro Axel's avatar
Caro Axel committed
134 135 136 137 138 139 140 141 142 143 144 145 146 147
	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
148
func (server *ttpServer) handleInvalidPromises(manager *entities.ArchivesManager, promises []*cAPI.Promise, senderIndex, stepIndex uint32) (bool, *tAPI.TTPResponse, []*entities.Promise, error) {
149
	valid, tmpPromises := entities.ArePromisesValid(promises)
Caro Axel's avatar
Caro Axel committed
150 151 152
	if valid {
		dAPI.DLog("received promises are valid")
	}
Caro Axel's avatar
Caro Axel committed
153
	complete := resolve.ArePromisesComplete(tmpPromises, promises[0], stepIndex)
Caro Axel's avatar
Caro Axel committed
154 155 156
	if complete {
		dAPI.DLog("received promises are complete")
	}
Caro Axel's avatar
Caro Axel committed
157
	if !valid || !complete {
Caro Axel's avatar
Caro Axel committed
158 159 160 161 162 163
		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
164 165 166
		manager.AddToAbort(senderIndex)
		manager.AddToDishonest(senderIndex)

Caro Axel's avatar
Caro Axel committed
167
		ok, err := manager.DB.Get("signatures").UpdateByID(*(manager.Archives))
Caro Axel's avatar
Caro Axel committed
168
		if !ok {
Caro Axel's avatar
Caro Axel committed
169
			dAPI.DLog("error during 'UpdateByID' l.132" + fmt.Sprint(err.Error()))
Caro Axel's avatar
Caro Axel committed
170 171 172
			return true, nil, nil, errors.New(InternalError)
		}

Caro Axel's avatar
Caro Axel committed
173
		dAPI.DLog("sending an abort token")
Caro Axel's avatar
Caro Axel committed
174 175 176 177 178 179 180 181 182 183 184 185
		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)
186
func (server *ttpServer) updateArchiveWithEvidence(manager *entities.ArchivesManager, tmpPromises []*entities.Promise) {
Caro Axel's avatar
Caro Axel committed
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
	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)
206
func (server *ttpServer) handleContractGenerationTry(manager *entities.ArchivesManager) (*tAPI.TTPResponse, error) {
Caro Axel's avatar
Caro Axel committed
207 208
	generated, contract := resolve.Solve(manager)
	if !generated {
Caro Axel's avatar
Caro Axel committed
209
		dAPI.DLog("contract couldn't be generated. Sending an abort token.")
Caro Axel's avatar
Caro Axel committed
210 211 212 213 214 215 216 217
		return &tAPI.TTPResponse{
			Abort:    true,
			Contract: nil,
		}, nil
	}

	// We add the generated contract to the signatureArchives
	manager.Archives.SignedContract = contract
Caro Axel's avatar
Caro Axel committed
218
	dAPI.DLog("contract was generated. Sending the signed contract.")
Caro Axel's avatar
Caro Axel committed
219 220 221 222
	return &tAPI.TTPResponse{
		Abort:    false,
		Contract: contract,
	}, nil
223 224
}

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

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

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

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

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

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