server.go 8.41 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 21 22
	"golang.org/x/net/context"
	"google.golang.org/grpc"
)

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

Caro Axel's avatar
Caro Axel committed
26 27
var mutex sync.Mutex

28
type ttpServer struct {
ElyKar's avatar
ElyKar committed
29
	DB *mgdb.MongoManager
30 31
}

Caro Axel's avatar
Caro Axel committed
32 33
// 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
34 35 36
	mutex.Lock()
	defer mutex.Unlock()

Caro Axel's avatar
Caro Axel committed
37 38
	valid, signatureUUID, signers, senderIndex := entities.IsRequestValid(ctx, in.Promises)
	if !valid {
39
		dAPI.DLog("invalid request from " + net.GetCN(&ctx))
Caro Axel's avatar
Caro Axel committed
40 41
		return nil, errors.New(InternalError)
	}
Caro Axel's avatar
Caro Axel committed
42 43 44

	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
45
	if !valid {
46
		dAPI.DLog("invalid sequence index from " + net.GetCN(&ctx))
Caro Axel's avatar
Caro Axel committed
47 48 49 50 51 52
		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
53 54
	dAPI.DLog("Resolve request from " + net.GetCN(&ctx) + " is valid")

55
	manager := entities.NewArchivesManager(server.DB)
Caro Axel's avatar
Caro Axel committed
56 57 58 59 60
	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
61 62 63 64 65
	// 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 {
66
		dAPI.DLog("already sent an abort token to " + net.GetCN(&ctx))
Caro Axel's avatar
Caro Axel committed
67 68 69 70
		return message, err
	}

	// We check that the sender of the request sent valid and complete information
Caro Axel's avatar
Caro Axel committed
71
	stop, message, tmpPromises, err := server.handleInvalidPromises(manager, in.Promises, senderIndex, in.Index)
Caro Axel's avatar
Caro Axel committed
72
	if stop {
Caro Axel's avatar
Caro Axel committed
73
		dAPI.DLog("invalid promise caused stop")
Caro Axel's avatar
Caro Axel committed
74 75 76 77 78 79 80
		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
81
		dAPI.DLog("sending the signed contract")
Caro Axel's avatar
Caro Axel committed
82 83 84 85 86 87 88 89 90 91 92 93
		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
94
	ok, err := server.DB.Get("signatures").UpdateByID(*(manager.Archives))
Caro Axel's avatar
Caro Axel committed
95
	if !ok {
Caro Axel's avatar
Caro Axel committed
96
		dAPI.DLog("error during 'UpdateByID' l.81" + fmt.Sprint(err.Error()))
Caro Axel's avatar
Caro Axel committed
97 98 99 100 101 102 103 104 105 106 107
		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.
108
func (server *ttpServer) handleAbortedSender(manager *entities.ArchivesManager, senderIndex uint32) (bool, *tAPI.TTPResponse, error) {
Caro Axel's avatar
Caro Axel committed
109
	if manager.HasReceivedAbortToken(senderIndex) {
Caro Axel's avatar
Caro Axel committed
110
		dAPI.DLog("Sender has already contacted the ttp. He is dishonnest.")
Caro Axel's avatar
Caro Axel committed
111 112
		manager.AddToDishonest(senderIndex)

Caro Axel's avatar
Caro Axel committed
113
		ok, err := manager.DB.Get("signatures").UpdateByID(*(manager.Archives))
Caro Axel's avatar
Caro Axel committed
114
		if !ok {
Caro Axel's avatar
Caro Axel committed
115
			dAPI.DLog("error during 'UpdateByID' l.99" + fmt.Sprint(err.Error()))
Caro Axel's avatar
Caro Axel committed
116 117 118 119 120 121 122 123
			return true, nil, errors.New(InternalError)
		}

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

Caro Axel's avatar
Caro Axel committed
158
		ok, err := manager.DB.Get("signatures").UpdateByID(*(manager.Archives))
Caro Axel's avatar
Caro Axel committed
159
		if !ok {
Caro Axel's avatar
Caro Axel committed
160
			dAPI.DLog("error during 'UpdateByID' l.132" + fmt.Sprint(err.Error()))
Caro Axel's avatar
Caro Axel committed
161 162 163
			return true, nil, nil, errors.New(InternalError)
		}

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

Caro Axel's avatar
Caro Axel committed
216 217 218
// Recover route for the TTP.
func (server *ttpServer) Recover(ctx context.Context, in *tAPI.RecoverRequest) (*tAPI.TTPResponse, error) {
	// TODO
219 220 221
	return nil, nil
}

Caro Axel's avatar
Caro Axel committed
222
// GetServer returns the gRPC server.
ElyKar's avatar
ElyKar committed
223 224
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
225 226
	entities.AuthContainer = security.NewAuthContainer(viper.GetString("password"))
	ca, cert, key, err := entities.AuthContainer.LoadFiles()
227
	if err != nil {
228
		fmt.Fprintln(os.Stderr, "An error occured during the private key and certificates retrieval:", err)
229 230 231
		os.Exit(1)
	}

ElyKar's avatar
ElyKar committed
232
	dbManager, err := mgdb.NewManager(viper.GetString("dbURI"))
233 234
	if err != nil {
		fmt.Fprintln(os.Stderr, "An error occured during the connection to MongoDB:", err)
235
		os.Exit(2)
236 237
	}

238
	server := &ttpServer{
ElyKar's avatar
ElyKar committed
239
		DB: dbManager,
240 241
	}
	netServer := net.NewServer(cert, key, ca)
Caro Axel's avatar
Caro Axel committed
242
	tAPI.RegisterTTPServer(netServer, server)
243 244
	return netServer
}