server.go 17.4 KB
Newer Older
1
package keyshareserver
2
3

import (
4
	"context"
5
6
7
	"fmt"
	"net/http"
	"strings"
8
	"time"
9

10
11
	"github.com/go-errors/errors"
	"github.com/hashicorp/go-multierror"
12
	"github.com/jasonlvhit/gocron"
13
14
15
16
17
	"github.com/privacybydesign/gabi"
	"github.com/privacybydesign/gabi/big"
	irma "github.com/privacybydesign/irmago"
	"github.com/sirupsen/logrus"

18
	"github.com/privacybydesign/irmago/internal/common"
19
	"github.com/privacybydesign/irmago/internal/keysharecore"
20
21
22
23
24
25
26
	"github.com/privacybydesign/irmago/server"
	"github.com/privacybydesign/irmago/server/irmaserver"

	"github.com/go-chi/chi"
)

type Server struct {
27
	// configuration
28
29
	conf *Configuration

30
	// external components
31
32
33
	core     *keysharecore.Core
	irmaserv *irmaserver.Server
	db       DB
34
35

	// Scheduler used to clean sessions
36
37
	scheduler     *gocron.Scheduler
	stopScheduler chan<- bool
38

39
	// Session data, keeping track of current keyshare protocol session state for each user
40
	store sessionStore
41
42
}

43
44
var errMissingCommitment = errors.New("missing previous call to getCommitments")

45
46
47
func New(conf *Configuration) (*Server, error) {
	var err error
	s := &Server{
48
		conf:      conf,
49
		store:     newMemorySessionStore(10 * time.Second),
50
		scheduler: gocron.NewScheduler(),
51
52
	}

53
	// Setup IRMA session server
54
	s.irmaserv, err = irmaserver.New(conf.Configuration)
55
56
57
58
	if err != nil {
		return nil, err
	}

59
	// Process configuration and create keyshare core
60
61
62
63
64
65
66
67
68
69
70
71
72
	err = validateConf(conf)
	if err != nil {
		return nil, err
	}
	if conf.DB != nil {
		s.db = conf.DB
	} else {
		s.db, err = setupDatabase(conf)
		if err != nil {
			return nil, err
		}
	}
	s.core, err = setupCore(conf)
73
74
75
76
	if err != nil {
		return nil, err
	}

77
	// Load Idemix keys into core, and ensure that new keys added in the future will be loaded as well.
78
	if err = s.loadIdemixKeys(conf.IrmaConfiguration); err != nil {
79
80
		return nil, err
	}
81
82
	conf.IrmaConfiguration.UpdateListeners = append(conf.IrmaConfiguration.UpdateListeners, func(c *irma.Configuration) {
		if err := s.loadIdemixKeys(c); err != nil {
83
84
85
86
			// run periodically; can only log the error here
			_ = server.LogError(err)
		}
	})
87

88
	// Setup session cache clearing
89
	s.scheduler.Every(10).Seconds().Do(s.store.flush)
90
91
	s.stopScheduler = s.scheduler.Start()

92
93
94
	return s, nil
}

95
96
func (s *Server) Stop() {
	s.stopScheduler <- true
97
	s.irmaserv.Stop()
98
99
}

100
101
func (s *Server) Handler() http.Handler {
	router := chi.NewRouter()
102

103
	router.Group(func(router chi.Router) {
104
105
106
		router.Use(server.SizeLimitMiddleware)
		router.Use(server.TimeoutMiddleware(nil, server.WriteTimeout))

107
108
109
110
111
		if s.conf.Verbose >= 2 {
			opts := server.LogOptions{Response: true, Headers: true, From: false, EncodeBinary: true}
			router.Use(server.LogMiddleware("keyshareserver", opts))
		}

112
113
114
115
116
117
118
119
120
121
122
123
124
125
		// Registration
		router.Post("/client/register", s.handleRegister)

		// Pin logic
		router.Post("/users/verify/pin", s.handleVerifyPin)
		router.Post("/users/change/pin", s.handleChangePin)

		// Keyshare sessions
		router.Group(func(router chi.Router) {
			router.Use(s.userMiddleware)
			router.Use(s.authorizationMiddleware)
			router.Post("/prove/getCommitments", s.handleCommitments)
			router.Post("/prove/getResponse", s.handleResponse)
		})
126
	})
127

128
	// IRMA server for issuing myirma credential during registration
129
	router.Mount("/irma/", s.irmaserv.HandlerFunc())
130
131
132
	return router
}

133
// On configuration changes, update the keyshare core with all current public keys of the IRMA issuers.
134
func (s *Server) loadIdemixKeys(conf *irma.Configuration) error {
135
	errs := multierror.Error{}
136
	for _, issuer := range conf.Issuers {
137
		keyIDs, err := conf.PublicKeyIndices(issuer.Identifier())
138
		if err != nil {
139
			errs.Errors = append(errs.Errors, errors.Errorf("issuer %v: could not find key IDs: %v", issuer, err))
140
141
			continue
		}
142
		for _, id := range keyIDs {
143
144
			key, err := conf.PublicKey(issuer.Identifier(), id)
			if err != nil {
145
				errs.Errors = append(errs.Errors, server.LogError(errors.Errorf("key %v-%v: could not fetch public key: %v", issuer, id, err)))
146
147
				continue
			}
148
			s.core.DangerousAddTrustedPublicKey(irma.PublicKeyIdentifier{Issuer: issuer.Identifier(), Counter: id}, key)
149
150
		}
	}
151
	return errs.ErrorOrNil()
152
153
}

154
// /prove/getCommitments
155
func (s *Server) handleCommitments(w http.ResponseWriter, r *http.Request) {
156
	// Fetch from context
157
	user := r.Context().Value("user").(*User)
158
159
	authorization := r.Context().Value("authorization").(string)

160
	// Read keys
161
	var keys []irma.PublicKeyIdentifier
162
	if err := server.ParseBody(r, &keys); err != nil {
163
164
165
166
		server.WriteError(w, server.ErrorInvalidRequest, err.Error())
		return
	}
	if len(keys) == 0 {
167
		s.conf.Logger.Info("Malformed request: no keys for commitment specified")
168
169
170
171
		server.WriteError(w, server.ErrorInvalidRequest, "No key specified")
		return
	}

172
	commitments, err := s.generateCommitments(user, authorization, keys)
173
	if err != nil && (err == keysharecore.ErrInvalidChallenge || err == keysharecore.ErrInvalidJWT) {
174
175
		server.WriteError(w, server.ErrorInvalidRequest, err.Error())
		return
176
	}
177
	if err != nil {
178
179
		// already logged
		server.WriteError(w, server.ErrorInternal, err.Error())
180
181
182
		return
	}

183
184
185
	server.WriteJson(w, commitments)
}

186
func (s *Server) generateCommitments(user *User, authorization string, keys []irma.PublicKeyIdentifier) (*irma.ProofPCommitmentMap, error) {
187
	// Generate commitments
188
	commitments, commitID, err := s.core.GenerateCommitments(user.Secrets, authorization, keys)
189
190
	if err != nil {
		s.conf.Logger.WithField("error", err).Warn("Could not generate commitments for request")
191
		return nil, err
192
193
194
	}

	// Prepare output message format
195
	mappedCommitments := map[irma.PublicKeyIdentifier]*gabi.ProofPCommitment{}
196
	for i, keyID := range keys {
197
		mappedCommitments[keyID] = commitments[i]
198
199
200
	}

	// Store needed data for later requests.
201
202
203
204
	// Of all keys involved in the current session, store the ID of the first one to be used when
	// the user comes back later to retrieve her response. gabi.ProofP.P will depend on this public
	// key, which is used only during issuance. Thus, this assumes that during issuance, the user
	// puts the key ID of the credential(s) being issued at index 0.
205
	s.store.add(user.Username, &session{
206
207
		KeyID:    keys[0],
		CommitID: commitID,
208
	})
209
210

	// And send response
211
	return &irma.ProofPCommitmentMap{Commitments: mappedCommitments}, nil
212
213
}

214
// /prove/getResponse
215
func (s *Server) handleResponse(w http.ResponseWriter, r *http.Request) {
216
	// Fetch from context
217
	user := r.Context().Value("user").(*User)
218
219
	authorization := r.Context().Value("authorization").(string)

220
221
	// Read challenge
	challenge := new(big.Int)
222
	if err := server.ParseBody(r, challenge); err != nil {
223
224
225
226
		server.WriteError(w, server.ErrorInvalidRequest, err.Error())
		return
	}

227
228
229
230
231
	// verify access (avoids leaking whether there is a session ongoing to unauthorized callers)
	if !r.Context().Value("hasValidAuthorization").(bool) {
		s.conf.Logger.Warn("Could not generate keyshare response due to invalid authorization")
		server.WriteError(w, server.ErrorInvalidRequest, "Invalid authorization")
		return
232
	}
233

234
	// And do the actual responding
235
236
237
238
239
	proofResponse, err := s.generateResponse(user, authorization, challenge)
	if err != nil &&
		(err == keysharecore.ErrInvalidChallenge ||
			err == keysharecore.ErrInvalidJWT ||
			err == errMissingCommitment) {
240
241
242
		server.WriteError(w, server.ErrorInvalidRequest, err.Error())
		return
	}
243
244
245
246
247
248
249
250
	if err != nil {
		// already logged
		server.WriteError(w, server.ErrorInternal, err.Error())
		return
	}

	server.WriteString(w, proofResponse)
}
251

252
253
254
255
256
257
258
259
func (s *Server) generateResponse(user *User, authorization string, challenge *big.Int) (string, error) {
	// Get data from session
	sessionData := s.store.get(user.Username)
	if sessionData == nil {
		s.conf.Logger.Warn("Request for response without previous call to get commitments")
		return "", errMissingCommitment
	}

David Venhoek's avatar
David Venhoek committed
260
	// Indicate activity on user account
261
	err := s.db.setSeen(user)
David Venhoek's avatar
David Venhoek committed
262
263
264
265
266
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not mark user as seen recently")
		// Do not send to user
	}

267
	// Make log entry
268
	err = s.db.addLog(user, eventTypeIRMASession, nil)
269
270
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not add log entry for user")
271
		return "", err
272
273
	}

274
	proofResponse, err := s.core.GenerateResponse(user.Secrets, authorization, sessionData.CommitID, challenge, sessionData.KeyID)
275
276
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not generate response for request")
277
		return "", err
278
279
	}

280
	return proofResponse, nil
281
282
}

283
// /users/verify/pin
284
285
func (s *Server) handleVerifyPin(w http.ResponseWriter, r *http.Request) {
	// Extract request
286
	var msg irma.KeysharePinMessage
287
	if err := server.ParseBody(r, &msg); err != nil {
288
289
290
291
292
		server.WriteError(w, server.ErrorInvalidRequest, err.Error())
		return
	}

	// Fetch user
293
	user, err := s.db.user(msg.Username)
294
295
296
297
298
299
	if err != nil {
		s.conf.Logger.WithFields(logrus.Fields{"username": msg.Username, "error": err}).Warn("Could not find user in db")
		server.WriteError(w, server.ErrorUserNotRegistered, "")
		return
	}

300
	// and verify pin
301
	result, err := s.verifyPin(user, msg.Pin)
302
	if err != nil {
303
		// already logged
304
305
306
		server.WriteError(w, server.ErrorInternal, err.Error())
		return
	}
307
308
309
310

	server.WriteJson(w, result)
}

311
func (s *Server) verifyPin(user *User, pin string) (irma.KeysharePinStatus, error) {
312
	// Check whether pin check is currently allowed
313
	ok, tries, wait, err := s.reservePinCheck(user)
314
	if err != nil {
315
		return irma.KeysharePinStatus{}, err
316
	}
317
	if !ok {
318
		return irma.KeysharePinStatus{Status: "error", Message: fmt.Sprintf("%v", wait)}, nil
319
	}
320

321
	// At this point, we are allowed to do an actual check (we have successfully reserved a spot for it), so do it.
322
	jwtt, err := s.core.ValidatePin(user.Secrets, pin)
323
324
325
	if err != nil && err != keysharecore.ErrInvalidPin {
		// Errors other than invalid pin are real errors
		s.conf.Logger.WithField("error", err).Error("Could not validate pin")
326
		return irma.KeysharePinStatus{}, err
327
	}
328

329
	if err == keysharecore.ErrInvalidPin {
330
		// Handle invalid pin
331
		err = s.db.addLog(user, eventTypePinCheckFailed, tries)
332
333
		if err != nil {
			s.conf.Logger.WithField("error", err).Error("Could not add log entry for user")
334
			return irma.KeysharePinStatus{}, err
335
		}
336
		if tries == 0 {
337
			err = s.db.addLog(user, eventTypePinCheckBlocked, wait)
338
339
			if err != nil {
				s.conf.Logger.WithField("error", err).Error("Could not add log entry for user")
340
				return irma.KeysharePinStatus{}, err
341
			}
342
			return irma.KeysharePinStatus{Status: "error", Message: fmt.Sprintf("%v", wait)}, nil
343
		} else {
344
			return irma.KeysharePinStatus{Status: "failure", Message: fmt.Sprintf("%v", tries)}, nil
345
		}
346
	}
347

348
	// Handle success
349
	err = s.db.resetPinTries(user)
350
351
352
353
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not reset users pin check logic")
		// Do not send to user
	}
354
	err = s.db.setSeen(user)
355
356
357
358
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not indicate user activity")
		// Do not send to user
	}
359
	err = s.db.addLog(user, eventTypePinCheckSuccess, nil)
360
361
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not add log entry for user")
362
		return irma.KeysharePinStatus{}, err
363
	}
364

365
	return irma.KeysharePinStatus{Status: "success", Message: jwtt}, err
366
367
}

368
// /users/change/pin
369
370
func (s *Server) handleChangePin(w http.ResponseWriter, r *http.Request) {
	// Extract request
371
	var msg irma.KeyshareChangePin
372
	if err := server.ParseBody(r, &msg); err != nil {
373
		server.WriteError(w, server.ErrorInvalidRequest, err.Error())
374
375
376
377
		return
	}

	// Fetch user
378
	user, err := s.db.user(msg.Username)
379
380
381
382
383
384
	if err != nil {
		s.conf.Logger.WithFields(logrus.Fields{"username": msg.Username, "error": err}).Warn("Could not find user in db")
		server.WriteError(w, server.ErrorUserNotRegistered, "")
		return
	}

385
	result, err := s.updatePin(user, msg.OldPin, msg.NewPin)
386
	if err != nil {
387
		// already logged
388
		server.WriteError(w, server.ErrorInternal, err.Error())
389
390
		return
	}
391
392
393
	server.WriteJson(w, result)
}

394
func (s *Server) updatePin(user *User, oldPin, newPin string) (irma.KeysharePinStatus, error) {
395
	// Check whether pin check is currently allowed
396
	ok, tries, wait, err := s.reservePinCheck(user)
397
	if err != nil {
398
		return irma.KeysharePinStatus{}, err
399
	}
400
	if !ok {
401
		return irma.KeysharePinStatus{Status: "error", Message: fmt.Sprintf("%v", wait)}, nil
402
	}
403
404

	// Try to do the update
405
	user.Secrets, err = s.core.ChangePin(user.Secrets, oldPin, newPin)
406
	if err == keysharecore.ErrInvalidPin {
407
		if tries == 0 {
408
			return irma.KeysharePinStatus{Status: "error", Message: fmt.Sprintf("%v", wait)}, nil
409
		} else {
410
			return irma.KeysharePinStatus{Status: "failure", Message: fmt.Sprintf("%v", tries)}, nil
411
412
413
		}
	} else if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not change pin")
414
		return irma.KeysharePinStatus{}, err
415
	}
416
417

	// Mark pincheck as success, resetting users wait and count
418
	err = s.db.resetPinTries(user)
419
420
421
422
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not reset users pin check logic")
		// Do not send to user
	}
423
424

	// Write user back
425
	err = s.db.updateUser(user)
426
427
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not write updated user to database")
428
		return irma.KeysharePinStatus{}, err
429
430
	}

431
	return irma.KeysharePinStatus{Status: "success"}, nil
432
433
}

434
// /client/register
435
436
func (s *Server) handleRegister(w http.ResponseWriter, r *http.Request) {
	// Extract request
437
	var msg irma.KeyshareEnrollment
438
	if err := server.ParseBody(r, &msg); err != nil {
439
		server.WriteError(w, server.ErrorInvalidRequest, err.Error())
440
441
442
		return
	}

443
	sessionptr, err := s.register(msg)
444
	if err != nil && err == keysharecore.ErrPinTooLong {
445
446
447
448
449
450
451
452
453
454
455
456
		// Too long pin is not an internal error
		server.WriteError(w, server.ErrorInvalidRequest, err.Error())
		return
	}
	if err != nil {
		// Already logged
		server.WriteError(w, server.ErrorInternal, err.Error())
		return
	}
	server.WriteJson(w, sessionptr)
}

457
func (s *Server) register(msg irma.KeyshareEnrollment) (*irma.Qr, error) {
458
	// Generate keyshare server account
459
	username := common.NewRandomString(12, common.AlphanumericChars)
460

461
	secrets, err := s.core.NewUserSecrets(msg.Pin)
462
463
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not register user")
464
		return nil, err
465
	}
466
	user := &User{Username: username, Language: msg.Language, Secrets: secrets}
467
	err = s.db.AddUser(user)
468
469
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not store new user in database")
470
		return nil, err
471
472
	}

473
	// Send email if user specified email address
474
	if msg.Email != nil && *msg.Email != "" && s.conf.EmailServer != "" {
475
		err = s.sendRegistrationEmail(user, msg.Language, *msg.Email)
476
		if err != nil {
477
478
			// already logged in sendRegistrationEmail
			return nil, err
479
		}
480
481
	}

482
483
484
	// Setup and return issuance session for keyshare credential.
	request := irma.NewIssuanceRequest([]*irma.CredentialRequest{
		{
485
			CredentialTypeID: s.conf.KeyshareAttribute.CredentialTypeIdentifier(),
486
			Attributes: map[string]string{
487
				s.conf.KeyshareAttribute.Name(): username,
488
489
			},
		}})
490
	sessionptr, _, _, err := s.irmaserv.StartSession(request, nil)
491
492
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not start keyshare credential issuance sessions")
493
		return nil, err
494
	}
495
496
497
	return sessionptr, nil
}

498
func (s *Server) sendRegistrationEmail(user *User, language, email string) error {
499
500
501
502
	// Generate token
	token := common.NewSessionToken()

	// Add it to the database
503
	err := s.db.addEmailVerification(user, email, token)
504
	if err != nil {
505
		s.conf.Logger.WithField("error", err).Error("Could not generate email verification mail record")
506
507
508
		return err
	}

509
510
511
	verificationBaseURL := s.conf.TranslateString(s.conf.VerificationURL, language)
	return s.conf.SendEmail(
		s.conf.registrationEmailTemplates,
512
		s.conf.RegistrationEmailSubjects,
513
514
515
516
		map[string]string{"VerificationURL": verificationBaseURL + token},
		[]string{email},
		language,
	)
517
518
}

519
520
521
522
523
524
func (s *Server) userMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Extract username from request
		username := r.Header.Get("X-IRMA-Keyshare-Username")

		// and fetch its information
525
		user, err := s.db.user(username)
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
		if err != nil {
			s.conf.Logger.WithFields(logrus.Fields{"username": username, "error": err}).Warn("Could not find user in db")
			server.WriteError(w, server.ErrorUserNotRegistered, err.Error())
			return
		}

		next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), "user", user)))
	})
}

func (s *Server) authorizationMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Extract authorization from request
		authorization := r.Header.Get("Authorization")
		if strings.HasPrefix(authorization, "Bearer ") {
			authorization = authorization[7:]
		}

		// verify access
		ctx := r.Context()
546
		err := s.core.ValidateJWT(ctx.Value("user").(*User).Secrets, authorization)
547
		hasValidAuthorization := err == nil
548
549
550
551
552
553
554
555
556

		// Construct new context with both authorization and its validity
		nextContext := context.WithValue(
			context.WithValue(ctx, "authorization", authorization),
			"hasValidAuthorization", hasValidAuthorization)

		next.ServeHTTP(w, r.WithContext(nextContext))
	})
}
557

558
func (s *Server) reservePinCheck(user *User) (bool, int, int64, error) {
559
	ok, tries, wait, err := s.db.reservePinTry(user)
560
561
562
563
564
	if err != nil {
		s.conf.Logger.WithField("error", err).Error("Could not reserve pin check slot")
		return false, 0, 0, err
	}
	if !ok {
565
		err = s.db.addLog(user, eventTypePinCheckRefused, nil)
566
567
568
569
570
571
572
573
		if err != nil {
			s.conf.Logger.WithField("error", err).Error("Could not add log entry for user")
			return false, 0, 0, err
		}
		return false, tries, wait, nil
	}
	return true, tries, wait, nil
}