session.go 19.1 KB
Newer Older
1
package irmaclient
2
3

import (
Sietse Ringers's avatar
Sietse Ringers committed
4
5
	"fmt"
	"sort"
6
	"strconv"
7
	"strings"
8
  "encoding/json"
9

10
11
	"math/big"

12
	"github.com/privacybydesign/irmago"
13
	"github.com/go-errors/errors"
14
	"github.com/mhe/gabi"
15
16
)

Sietse Ringers's avatar
Sietse Ringers committed
17
18
19
// This file contains the client side of the IRMA protocol, as well as the Handler interface
// which is used to communicate session info with the user.

Sietse Ringers's avatar
Sietse Ringers committed
20
21
// PermissionHandler is a callback for providing permission for an IRMA session
// and specifying the attributes to be disclosed.
22
type PermissionHandler func(proceed bool, choice *irma.DisclosureChoice)
23

Sietse Ringers's avatar
Sietse Ringers committed
24
// PinHandler is used to provide the user's PIN code.
25
26
type PinHandler func(proceed bool, pin string)

27
28
// A Handler contains callbacks for communication to the user.
type Handler interface {
29
30
31
32
33
34
35
36
37
38
39
	StatusUpdate(action irma.Action, status irma.Status)
	Success(action irma.Action)
	Cancelled(action irma.Action)
	Failure(action irma.Action, err *irma.SessionError)
	UnsatisfiableRequest(action irma.Action, missing irma.AttributeDisjunctionList)
	MissingKeyshareEnrollment(manager irma.SchemeManagerIdentifier)

	RequestIssuancePermission(request irma.IssuanceRequest, ServerName string, callback PermissionHandler)
	RequestVerificationPermission(request irma.DisclosureRequest, ServerName string, callback PermissionHandler)
	RequestSignaturePermission(request irma.SignatureRequest, ServerName string, callback PermissionHandler)
	RequestSchemeManagerPermission(manager *irma.SchemeManager, callback func(proceed bool))
Sietse Ringers's avatar
Sietse Ringers committed
40

41
	RequestPin(remainingAttempts int, callback PinHandler)
42
43
}

Sietse Ringers's avatar
Sietse Ringers committed
44
// SessionDismisser can dismiss the current IRMA session.
45
46
47
48
type SessionDismisser interface {
	Dismiss()
}

Sietse Ringers's avatar
Sietse Ringers committed
49
50
// A session is an IRMA session.
type session struct {
51
52
	Action    irma.Action
	Version   irma.Version
53
54
55
	ServerURL string
	Handler   Handler

56
	info        *irma.SessionInfo
57
	client      *Client
58
59
60
61
62
	jwt         irma.RequestorJwt
	irmaSession irma.IrmaSession
	transport   *irma.HTTPTransport
	choice      *irma.DisclosureChoice
	downloaded  *irma.IrmaIdentifierSet
63
	done        bool
64
65
}

66
67
68
69
70
71
72
73
74
type manualSession struct {
	Action    irma.Action
	Handler     Handler
	client      *Client
	request     *irma.SignatureRequest
	version  irma.Version
	choice      *irma.DisclosureChoice
}

75
76
// We implement the handler for the keyshare protocol
var _ keyshareSessionHandler = (*session)(nil)
77
var _ keyshareSessionHandler = (*manualSession)(nil)
78

79
80
// Supported protocol versions. Minor version numbers should be reverse sorted.
var supportedVersions = map[int][]int{
81
	2: {2, 1},
82
83
}

84
func calcVersion(qr *irma.Qr) (string, error) {
85
	// Parse range supported by server
86
87
88
89
90
91
92
93
94
95
96
97
	var minmajor, minminor, maxmajor, maxminor int
	var err error
	if minmajor, err = strconv.Atoi(string(qr.ProtocolVersion[0])); err != nil {
		return "", err
	}
	if minminor, err = strconv.Atoi(string(qr.ProtocolVersion[2])); err != nil {
		return "", err
	}
	if maxmajor, err = strconv.Atoi(string(qr.ProtocolMaxVersion[0])); err != nil {
		return "", err
	}
	if maxminor, err = strconv.Atoi(string(qr.ProtocolMaxVersion[2])); err != nil {
98
99
100
101
102
		return "", err
	}

	// Iterate supportedVersions in reverse sorted order (i.e. biggest major number first)
	keys := make([]int, 0, len(supportedVersions))
103
	for k := range supportedVersions {
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
		keys = append(keys, k)
	}
	sort.Sort(sort.Reverse(sort.IntSlice(keys)))
	for _, major := range keys {
		for _, minor := range supportedVersions[major] {
			aboveMinimum := major > minmajor || (major == minmajor && minor >= minminor)
			underMaximum := major < maxmajor || (major == maxmajor && minor <= maxminor)
			if aboveMinimum && underMaximum {
				return fmt.Sprintf("%d.%d", major, minor), nil
			}
		}
	}
	return "", fmt.Errorf("No supported protocol version between %s and %s", qr.ProtocolVersion, qr.ProtocolMaxVersion)
}

119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
func parseSigrequestJSON(sigrequestJSONString string) (*irma.SignatureRequest, error) {
  sigrequestJSON := []byte(sigrequestJSONString)
  sigrequest := &irma.SignatureRequest{}
  err := json.Unmarshal(sigrequestJSON, sigrequest)

  return sigrequest, err
}

// Start a manual session
func (client *Client) NewManualSession(sigrequestJSONString string, handler Handler) {
	sigrequest, err := parseSigrequestJSON(sigrequestJSONString);
	if err != nil {
		handler.Failure(irma.ActionUnknown, &irma.SessionError{Err: err})
		return
	}

	session := &manualSession{
		Action: irma.ActionSigning, // TODO hardcoded for now
		Handler:  handler,
		client:   client,
		request:  sigrequest,
		version:  irma.Version("2"), // TODO hardcoded for now
	}

	// Check if we are enrolled into all involved keyshare servers
	for id := range session.request.Identifiers().SchemeManagers {
		manager, ok := session.client.Configuration.SchemeManagers[id]
		if !ok {
			session.Handler.Failure(session.Action, &irma.SessionError{ErrorType: irma.ErrorUnknownSchemeManager, Info: id.String()})
			return
		}
		distributed := manager.Distributed()
		_, enrolled := session.client.keyshareServers[id]
		if distributed && !enrolled {
			session.Handler.MissingKeyshareEnrollment(id)
			return
		}
	}

	candidates, missing := session.client.CheckSatisfiability(session.request.ToDisclose())
	if len(missing) > 0 {
		session.Handler.UnsatisfiableRequest(session.Action, missing)
		// TODO: session.transport.Delete() on dialog cancel
		return
	}
	session.request.SetCandidates(candidates)

	// Ask for permission to execute the session
	callback := PermissionHandler(func(proceed bool, choice *irma.DisclosureChoice) {
		session.choice = choice
		session.request.SetDisclosureChoice(choice)
		fmt.Println("Starting session.do()")
		 go session.do(proceed)
	})
	session.Handler.RequestSignaturePermission(
		*session.request, "TODO: NAME", callback)

	fmt.Println(session)
}

func (session *manualSession) do(proceed bool) {
	if !proceed {
		fmt.Println("Cancel..")
		session.Handler.Cancelled(session.Action)
		return
	}

	fmt.Println("Do manual session for session: ")
	if !session.request.Identifiers().Distributed(session.client.Configuration) {
		fmt.Println("No keyshare session needed...")
		var message interface{}
		var err error

		message, err = session.client.Proofs(session.choice, session.request, true)
		if err != nil {
			session.Handler.Failure(session.Action, &irma.SessionError{ErrorType: irma.ErrorCrypto, Err: err})
			return
		}

		mar, _ := json.Marshal(message)
		fmt.Println(string(mar))

		session.Handler.Success(session.Action)
	} else {
		fmt.Println("Starting keyshare session...")
		var builders gabi.ProofBuilderList
		var err error

		builders, err = session.client.ProofBuilders(session.choice)
		if err != nil {
			session.Handler.Failure(session.Action, &irma.SessionError{ErrorType: irma.ErrorCrypto, Err: err})
		}

		startKeyshareSession(
			session,
			session.Handler,
			builders,
			session.request,
			session.client.Configuration,
			session.client.keyshareServers,
			session.client.state,
		)
		fmt.Println("DONE")
	}
}

225
// NewSession creates and starts a new IRMA session.
226
func (client *Client) NewSession(qr *irma.Qr, handler Handler) SessionDismisser {
Sietse Ringers's avatar
Sietse Ringers committed
227
	session := &session{
228
		Action:    irma.Action(qr.Type),
229
230
		ServerURL: qr.URL,
		Handler:   handler,
231
		transport: irma.NewHTTPTransport(qr.URL),
232
		client:    client,
233
	}
234
235
236
237
238
239

	if session.Action == irma.ActionSchemeManager {
		go session.managerSession()
		return session
	}

240
241
	version, err := calcVersion(qr)
	if err != nil {
242
		session.fail(&irma.SessionError{ErrorType: irma.ErrorProtocolVersionNotSupported, Err: err})
243
		return nil
244
	}
245
	session.Version = irma.Version(version)
246
247
248

	// Check if the action is one of the supported types
	switch session.Action {
249
250
251
252
	case irma.ActionDisclosing: // nop
	case irma.ActionSigning: // nop
	case irma.ActionIssuing: // nop
	case irma.ActionUnknown:
253
254
		fallthrough
	default:
255
		session.fail(&irma.SessionError{ErrorType: irma.ErrorUnknownAction, Info: string(session.Action)})
256
		return nil
257
258
259
260
261
262
263
264
	}

	if !strings.HasSuffix(session.ServerURL, "/") {
		session.ServerURL += "/"
	}

	go session.start()

265
	return session
266
267
}

268
269
// start retrieves the first message in the IRMA protocol, checks if we can perform
// the request, and informs the user of the outcome.
Sietse Ringers's avatar
Sietse Ringers committed
270
func (session *session) start() {
271
	defer func() {
272
		if e := recover(); e != nil {
273
			if session.Handler != nil {
274
				session.Handler.Failure(session.Action, panicToError(e))
275
			}
276
		}
277
278
	}()

279
	session.Handler.StatusUpdate(session.Action, irma.StatusCommunicating)
280

Sietse Ringers's avatar
Sietse Ringers committed
281
	// Get the first IRMA protocol message and parse it
282
	session.info = &irma.SessionInfo{}
Sietse Ringers's avatar
Sietse Ringers committed
283
	Err := session.transport.Get("jwt", session.info)
Sietse Ringers's avatar
Sietse Ringers committed
284
	if Err != nil {
285
		session.fail(Err.(*irma.SessionError))
286
287
288
		return
	}

Sietse Ringers's avatar
Sietse Ringers committed
289
	var err error
290
	session.jwt, err = irma.ParseRequestorJwt(session.Action, session.info.Jwt)
Sietse Ringers's avatar
Sietse Ringers committed
291
	if err != nil {
292
		session.fail(&irma.SessionError{ErrorType: irma.ErrorInvalidJWT, Err: err})
Sietse Ringers's avatar
Sietse Ringers committed
293
294
		return
	}
Sietse Ringers's avatar
Sietse Ringers committed
295
	session.irmaSession = session.jwt.IrmaSession()
Sietse Ringers's avatar
Sietse Ringers committed
296
297
	session.irmaSession.SetContext(session.info.Context)
	session.irmaSession.SetNonce(session.info.Nonce)
298
299
	if session.Action == irma.ActionIssuing {
		ir := session.irmaSession.(*irma.IssuanceRequest)
300
		// Store which public keys the server will use
301
		for _, credreq := range ir.Credentials {
302
			credreq.KeyCounter = session.info.Keys[credreq.CredentialTypeID.IssuerIdentifier()]
303
304
305
		}
	}

306
	// Check if we are enrolled into all involved keyshare servers
Sietse Ringers's avatar
Sietse Ringers committed
307
	for id := range session.irmaSession.Identifiers().SchemeManagers {
308
		manager, ok := session.client.Configuration.SchemeManagers[id]
309
		if !ok {
310
			session.fail(&irma.SessionError{ErrorType: irma.ErrorUnknownSchemeManager, Info: id.String()})
311
312
313
			return
		}
		distributed := manager.Distributed()
314
		_, enrolled := session.client.keyshareServers[id]
315
		if distributed && !enrolled {
316
			session.delete()
317
			session.Handler.MissingKeyshareEnrollment(id)
Sietse Ringers's avatar
Sietse Ringers committed
318
319
320
321
			return
		}
	}

Sietse Ringers's avatar
Sietse Ringers committed
322
	// Download missing credential types/issuers/public keys from the scheme manager
323
	if session.downloaded, err = session.client.Configuration.Download(session.irmaSession.Identifiers()); err != nil {
324
325
		session.Handler.Failure(
			session.Action,
326
			&irma.SessionError{ErrorType: irma.ErrorConfigurationDownload, Err: err},
327
328
329
330
		)
		return
	}

331
332
	if session.Action == irma.ActionIssuing {
		ir := session.irmaSession.(*irma.IssuanceRequest)
Sietse Ringers's avatar
Sietse Ringers committed
333
		for _, credreq := range ir.Credentials {
334
			info, err := credreq.Info(session.client.Configuration)
335
			if err != nil {
336
				session.fail(&irma.SessionError{ErrorType: irma.ErrorUnknownCredentialType, Err: err})
337
338
339
				return
			}
			ir.CredentialInfoList = append(ir.CredentialInfoList, info)
340
341
342
		}
	}

343
	candidates, missing := session.client.CheckSatisfiability(session.irmaSession.ToDisclose())
344
345
	if len(missing) > 0 {
		session.Handler.UnsatisfiableRequest(session.Action, missing)
346
		// TODO: session.transport.Delete() on dialog cancel
347
348
		return
	}
349
	session.irmaSession.SetCandidates(candidates)
350

Sietse Ringers's avatar
Sietse Ringers committed
351
	// Ask for permission to execute the session
352
	callback := PermissionHandler(func(proceed bool, choice *irma.DisclosureChoice) {
Sietse Ringers's avatar
Sietse Ringers committed
353
354
355
		session.choice = choice
		session.irmaSession.SetDisclosureChoice(choice)
		go session.do(proceed)
356
	})
357
	session.Handler.StatusUpdate(session.Action, irma.StatusConnected)
358
	switch session.Action {
359
	case irma.ActionDisclosing:
360
		session.Handler.RequestVerificationPermission(
361
362
			*session.irmaSession.(*irma.DisclosureRequest), session.jwt.Requestor(), callback)
	case irma.ActionSigning:
363
		session.Handler.RequestSignaturePermission(
364
365
			*session.irmaSession.(*irma.SignatureRequest), session.jwt.Requestor(), callback)
	case irma.ActionIssuing:
366
		session.Handler.RequestIssuancePermission(
367
			*session.irmaSession.(*irma.IssuanceRequest), session.jwt.Requestor(), callback)
368
369
370
371
372
	default:
		panic("Invalid session type") // does not happen, session.Action has been checked earlier
	}
}

Sietse Ringers's avatar
Sietse Ringers committed
373
func (session *session) do(proceed bool) {
374
	defer func() {
375
		if e := recover(); e != nil {
376
			if session.Handler != nil {
377
				session.Handler.Failure(session.Action, panicToError(e))
378
			}
379
		}
380
381
	}()

382
	if !proceed {
383
		session.cancel()
384
385
		return
	}
386
	session.Handler.StatusUpdate(session.Action, irma.StatusCommunicating)
387

388
	if !session.irmaSession.Identifiers().Distributed(session.client.Configuration) {
Sietse Ringers's avatar
Sietse Ringers committed
389
390
391
		var message interface{}
		var err error
		switch session.Action {
392
		case irma.ActionSigning:
393
			message, err = session.client.Proofs(session.choice, session.irmaSession, true)
394
		case irma.ActionDisclosing:
395
			message, err = session.client.Proofs(session.choice, session.irmaSession, false)
396
397
		case irma.ActionIssuing:
			message, err = session.client.IssueCommitments(session.irmaSession.(*irma.IssuanceRequest))
Sietse Ringers's avatar
Sietse Ringers committed
398
399
		}
		if err != nil {
400
			session.fail(&irma.SessionError{ErrorType: irma.ErrorCrypto, Err: err})
Sietse Ringers's avatar
Sietse Ringers committed
401
402
403
404
			return
		}
		session.sendResponse(message)
	} else {
405
		var builders gabi.ProofBuilderList
Sietse Ringers's avatar
Sietse Ringers committed
406
407
		var err error
		switch session.Action {
408
		case irma.ActionSigning:
Sietse Ringers's avatar
Sietse Ringers committed
409
			fallthrough
410
		case irma.ActionDisclosing:
411
			builders, err = session.client.ProofBuilders(session.choice)
412
413
		case irma.ActionIssuing:
			builders, err = session.client.IssuanceProofBuilders(session.irmaSession.(*irma.IssuanceRequest))
Sietse Ringers's avatar
Sietse Ringers committed
414
415
		}
		if err != nil {
416
			session.fail(&irma.SessionError{ErrorType: irma.ErrorCrypto, Err: err})
Sietse Ringers's avatar
Sietse Ringers committed
417
418
		}

419
420
421
422
423
		startKeyshareSession(
			session,
			session.Handler,
			builders,
			session.irmaSession,
424
			session.client.Configuration,
425
			session.client.keyshareServers,
426
			session.client.state,
427
		)
428
	}
Sietse Ringers's avatar
Sietse Ringers committed
429
}
430

Sietse Ringers's avatar
Sietse Ringers committed
431
432
433
434
func (session *session) KeyshareDone(message interface{}) {
	session.sendResponse(message)
}

435
436
437
438
439
440
func (session *manualSession) KeyshareDone(message interface{}) {
	messageString, _ := json.Marshal(message)
	fmt.Println(string(messageString))
	session.Handler.Success(session.Action)
}

Sietse Ringers's avatar
Sietse Ringers committed
441
func (session *session) KeyshareCancelled() {
442
	session.cancel()
Sietse Ringers's avatar
Sietse Ringers committed
443
444
}

445
446
447
448
func (session *manualSession) KeyshareCancelled() {
	session.Handler.Cancelled(session.Action)
}

Sietse Ringers's avatar
Sietse Ringers committed
449
func (session *session) KeyshareBlocked(duration int) {
450
	session.fail(&irma.SessionError{ErrorType: irma.ErrorKeyshareBlocked, Info: strconv.Itoa(duration)})
Sietse Ringers's avatar
Sietse Ringers committed
451
452
}

453
454
455
456
func (session *manualSession) KeyshareBlocked(duration int) {
	session.Handler.Failure(session.Action, &irma.SessionError{ErrorType: irma.ErrorKeyshareBlocked, Info: strconv.Itoa(duration)})
}

Sietse Ringers's avatar
Sietse Ringers committed
457
func (session *session) KeyshareError(err error) {
458
459
460
461
462
463
464
465
	var serr *irma.SessionError
	var ok bool
	if serr, ok = err.(*irma.SessionError); !ok {
		serr = &irma.SessionError{ErrorType: irma.ErrorKeyshare, Err: err}
	} else {
		serr.ErrorType = irma.ErrorKeyshare
	}
	session.fail(serr)
Sietse Ringers's avatar
Sietse Ringers committed
466
467
}

468
469
470
471
472
473
474
475
476
477
478
func (session *manualSession) KeyshareError(err error) {
	var serr *irma.SessionError
	var ok bool
	if serr, ok = err.(*irma.SessionError); !ok {
		serr = &irma.SessionError{ErrorType: irma.ErrorKeyshare, Err: err}
	} else {
		serr.ErrorType = irma.ErrorKeyshare
	}
	session.Handler.Failure(session.Action, serr)
}

Sietse Ringers's avatar
Sietse Ringers committed
479
480
481
func (session *session) KeysharePin() {
	session.Handler.StatusUpdate(session.Action, irma.StatusConnected)
}
482
483
484
func (session *manualSession) KeysharePin() {
	session.Handler.StatusUpdate(session.Action, irma.StatusConnected)
}
Sietse Ringers's avatar
Sietse Ringers committed
485
486
487
488

func (session *session) KeysharePinOK() {
	session.Handler.StatusUpdate(session.Action, irma.StatusCommunicating)
}
489
490
491
func (session *manualSession) KeysharePinOK() {
	session.Handler.StatusUpdate(session.Action, irma.StatusCommunicating)
}
Sietse Ringers's avatar
Sietse Ringers committed
492

Sietse Ringers's avatar
Sietse Ringers committed
493
494
type disclosureResponse string

Sietse Ringers's avatar
Sietse Ringers committed
495
func (session *session) sendResponse(message interface{}) {
Sietse Ringers's avatar
Sietse Ringers committed
496
	var log *LogEntry
Sietse Ringers's avatar
Sietse Ringers committed
497
	var err error
Sietse Ringers's avatar
Sietse Ringers committed
498

Sietse Ringers's avatar
Sietse Ringers committed
499
	switch session.Action {
500
	case irma.ActionSigning:
Sietse Ringers's avatar
Sietse Ringers committed
501
		fallthrough
502
	case irma.ActionDisclosing:
Sietse Ringers's avatar
Sietse Ringers committed
503
		var response disclosureResponse
Sietse Ringers's avatar
Sietse Ringers committed
504
		if err = session.transport.Post("proofs", &response, message); err != nil {
505
			session.fail(err.(*irma.SessionError))
Sietse Ringers's avatar
Sietse Ringers committed
506
507
508
			return
		}
		if response != "VALID" {
509
			session.fail(&irma.SessionError{ErrorType: irma.ErrorRejected, Info: string(response)})
Sietse Ringers's avatar
Sietse Ringers committed
510
511
			return
		}
Sietse Ringers's avatar
Sietse Ringers committed
512
		log, _ = session.createLogEntry(message.(gabi.ProofList)) // TODO err
513
	case irma.ActionIssuing:
Sietse Ringers's avatar
Sietse Ringers committed
514
		response := []*gabi.IssueSignatureMessage{}
Sietse Ringers's avatar
Sietse Ringers committed
515
		if err = session.transport.Post("commitments", &response, message); err != nil {
516
			session.fail(err.(*irma.SessionError))
Sietse Ringers's avatar
Sietse Ringers committed
517
518
			return
		}
519
520
		if err = session.client.ConstructCredentials(response, session.irmaSession.(*irma.IssuanceRequest)); err != nil {
			session.fail(&irma.SessionError{ErrorType: irma.ErrorCrypto, Err: err})
Sietse Ringers's avatar
Sietse Ringers committed
521
522
			return
		}
Sietse Ringers's avatar
Sietse Ringers committed
523
		log, _ = session.createLogEntry(message) // TODO err
524
525
	}

526
	_ = session.client.addLogEntry(log) // TODO err
527
	if !session.downloaded.Empty() {
528
		session.client.handler.UpdateConfiguration(session.downloaded)
529
	}
530
	if session.Action == irma.ActionIssuing {
531
		session.client.handler.UpdateAttributes()
532
	}
533
	session.done = true
534
	session.Handler.Success(session.Action)
535
}
536
537

func (session *session) managerSession() {
538
	defer func() {
539
		if e := recover(); e != nil {
540
			if session.Handler != nil {
541
				session.Handler.Failure(session.Action, panicToError(e))
542
			}
543
		}
544
545
546
547
548
	}()

	// We have to download the scheme manager description.xml here before installing it,
	// because we need to show its contents (name, description, website) to the user
	// when asking installation permission.
549
	manager, err := session.client.Configuration.DownloadSchemeManager(session.ServerURL)
550
	if err != nil {
551
		session.Handler.Failure(session.Action, &irma.SessionError{ErrorType: irma.ErrorConfigurationDownload, Err: err})
552
553
		return
	}
554

555
	session.Handler.RequestSchemeManagerPermission(manager, func(proceed bool) {
556
		if !proceed {
557
			session.Handler.Cancelled(session.Action) // No need to DELETE session here
558
559
			return
		}
560
		if err := session.client.Configuration.AddSchemeManager(manager); err != nil {
561
			session.Handler.Failure(session.Action, &irma.SessionError{ErrorType: irma.ErrorConfigurationDownload, Err: err})
Sietse Ringers's avatar
Sietse Ringers committed
562
563
			return
		}
564
565

		// Update state and inform user of success
566
		if manager.Distributed() {
567
			session.client.UnenrolledSchemeManagers = session.client.unenrolledSchemeManagers()
568
		}
569
		session.client.handler.UpdateConfiguration(
570
571
572
573
			&irma.IrmaIdentifierSet{
				SchemeManagers:  map[irma.SchemeManagerIdentifier]struct{}{manager.Identifier(): {}},
				Issuers:         map[irma.IssuerIdentifier]struct{}{},
				CredentialTypes: map[irma.CredentialTypeIdentifier]struct{}{},
Sietse Ringers's avatar
Sietse Ringers committed
574
575
576
			},
		)
		session.Handler.Success(session.Action)
577
578
579
	})
	return
}
580
581
582

// Session lifetime functions

583
584
585
586
587
588
589
590
591
592
func panicToError(e interface{}) *irma.SessionError {
	var info string
	switch x := e.(type) {
	case string:
		info = x
	case error:
		info = x.Error()
	case fmt.Stringer:
		info = x.String()
	default: // nop
593
	}
594
	return &irma.SessionError{ErrorType: irma.ErrorPanic, Info: info}
595
596
597
598
}

// Idempotently send DELETE to remote server, returning whether or not we did something
func (session *session) delete() bool {
599
	if !session.done {
600
		session.transport.Delete()
601
		session.done = true
602
603
604
605
606
		return true
	}
	return false
}

607
func (session *session) fail(err *irma.SessionError) {
608
609
610
	if session.delete() {
		err.Err = errors.Wrap(err.Err, 0)
		if session.downloaded != nil && !session.downloaded.Empty() {
611
			session.client.handler.UpdateConfiguration(session.downloaded)
612
613
614
615
616
617
618
619
		}
		session.Handler.Failure(session.Action, err)
	}
}

func (session *session) cancel() {
	if session.delete() {
		if session.downloaded != nil && !session.downloaded.Empty() {
620
			session.client.handler.UpdateConfiguration(session.downloaded)
621
622
623
624
625
626
627
628
		}
		session.Handler.Cancelled(session.Action)
	}
}

func (session *session) Dismiss() {
	session.cancel()
}
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644

type issuanceState struct {
	nonce2   *big.Int
	builders []*gabi.CredentialBuilder
}

func newIssuanceState() (*issuanceState, error) {
	nonce2, err := gabi.RandomBigInt(gabi.DefaultSystemParameters[4096].Lstatzk)
	if err != nil {
		return nil, err
	}
	return &issuanceState{
		nonce2:   nonce2,
		builders: []*gabi.CredentialBuilder{},
	}, nil
}