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

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

10
11
	"math/big"

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

Sietse Ringers's avatar
Sietse Ringers committed
17
18
19
// This file contains the logic and state of performing IRMA sessions, communicates
// with IRMA API servers, and uses the calling Client to construct messages and replies
// in the IRMA protocol.
Sietse Ringers's avatar
Sietse Ringers committed
20

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

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

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

	KeyshareBlocked(manager irma.SchemeManagerIdentifier, duration int)
37
	KeyshareEnrollmentIncomplete(manager irma.SchemeManagerIdentifier)
38
	KeyshareEnrollmentMissing(manager irma.SchemeManagerIdentifier)
39
40
41
42
43

	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
44

45
	RequestPin(remainingAttempts int, callback PinHandler)
46
47
}

Sietse Ringers's avatar
Sietse Ringers committed
48
// SessionDismisser can dismiss the current IRMA session.
49
50
51
52
type SessionDismisser interface {
	Dismiss()
}

53
54
55
56
57
58
59
60
61
// getMetadataVersion maps a chosen protocol version to a metadata version that
// the server will use.
func getMetadataVersion(v *irma.ProtocolVersion) byte {
	if v.Below(2, 3) {
		return 0x02 // no support for optional attributes
	}
	return 0x03 // current version
}

Sietse Ringers's avatar
Sietse Ringers committed
62
type session struct {
Koen van Ingen's avatar
Koen van Ingen committed
63
64
	Action  irma.Action
	Handler Handler
65
	Version *irma.ProtocolVersion
66
67
68

	choice      *irma.DisclosureChoice
	client      *Client
Koen van Ingen's avatar
Koen van Ingen committed
69
	downloaded  *irma.IrmaIdentifierSet
70
	irmaSession irma.IrmaSession
71
	done        bool
72

73
	// These are empty on manual sessions
74
	ServerURL string
Koen van Ingen's avatar
Koen van Ingen committed
75
76
77
	info      *irma.SessionInfo
	jwt       irma.RequestorJwt
	transport *irma.HTTPTransport
78
79
}

80
// We implement the handler for the keyshare protocol
81
var _ keyshareSessionHandler = (*session)(nil)
82

83
84
// Supported protocol versions. Minor version numbers should be reverse sorted.
var supportedVersions = map[int][]int{
85
	2: {3, 2, 1},
86
87
}

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

	// Iterate supportedVersions in reverse sorted order (i.e. biggest major number first)
	keys := make([]int, 0, len(supportedVersions))
107
	for k := range supportedVersions {
108
109
110
111
112
113
114
115
		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 {
116
				return irma.NewVersion(major, minor), nil
117
118
119
			}
		}
	}
120
	return nil, fmt.Errorf("No supported protocol version between %s and %s", qr.ProtocolVersion, qr.ProtocolMaxVersion)
121
122
}

123
124
125
126
func (session *session) IsInteractive() bool {
	return session.ServerURL != ""
}

Koen van Ingen's avatar
Koen van Ingen committed
127
128
129
130
131
132
133
134
135
136
137
138
func (session *session) getBuilders() (gabi.ProofBuilderList, error) {
	var builders gabi.ProofBuilderList
	var err error

	switch session.Action {
	case irma.ActionSigning:
		fallthrough
	case irma.ActionDisclosing:
		builders, err = session.client.ProofBuilders(session.choice)
	case irma.ActionIssuing:
		builders, err = session.client.IssuanceProofBuilders(session.irmaSession.(*irma.IssuanceRequest))
	}
139

Koen van Ingen's avatar
Koen van Ingen committed
140
	return builders, err
141
142
}

Koen van Ingen's avatar
Koen van Ingen committed
143
144
145
func (session *session) getProof() (interface{}, error) {
	var message interface{}
	var err error
146

Koen van Ingen's avatar
Koen van Ingen committed
147
148
149
150
151
152
153
154
	switch session.Action {
	case irma.ActionSigning:
		message, err = session.client.Proofs(session.choice, session.irmaSession, true)
	case irma.ActionDisclosing:
		message, err = session.client.Proofs(session.choice, session.irmaSession, false)
	case irma.ActionIssuing:
		message, err = session.client.IssueCommitments(session.irmaSession.(*irma.IssuanceRequest))
	}
155

Koen van Ingen's avatar
Koen van Ingen committed
156
157
	return message, err
}
158

159
160
// checkKeyshareEnrollment checks if we are enrolled into all involved keyshare servers,
// and aborts the session if not
Koen van Ingen's avatar
Koen van Ingen committed
161
func (session *session) checkKeyshareEnrollment() bool {
162
	for id := range session.irmaSession.Identifiers().SchemeManagers {
163
164
165
		manager, ok := session.client.Configuration.SchemeManagers[id]
		if !ok {
			session.Handler.Failure(session.Action, &irma.SessionError{ErrorType: irma.ErrorUnknownSchemeManager, Info: id.String()})
Koen van Ingen's avatar
Koen van Ingen committed
166
			return false
167
168
169
170
		}
		distributed := manager.Distributed()
		_, enrolled := session.client.keyshareServers[id]
		if distributed && !enrolled {
171
			session.Handler.KeyshareEnrollmentMissing(id)
Koen van Ingen's avatar
Koen van Ingen committed
172
			return false
173
174
		}
	}
Koen van Ingen's avatar
Koen van Ingen committed
175
176
177
178
179
180
181
182
183
184
185
	return true
}

func (session *session) panicFailure() {
	if e := recover(); e != nil {
		if session.Handler != nil {
			session.Handler.Failure(session.Action, panicToError(e))
		}
	}
}

186
func (session *session) checkAndUpateConfiguration() bool {
187
188
	var err error
	for id := range session.irmaSession.Identifiers().SchemeManagers {
189
		manager, contains := session.client.Configuration.SchemeManagers[id]
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
		if !contains {
			session.fail(&irma.SessionError{
				ErrorType: irma.ErrorUnknownSchemeManager,
				Info:      id.String(),
			})
			return false
		}
		if !manager.Valid {
			session.fail(&irma.SessionError{
				ErrorType: irma.ErrorInvalidSchemeManager,
				Info:      string(manager.Status),
			})
			return false
		}
	}

	// Check if we are enrolled into all involved keyshare servers
	if !session.checkKeyshareEnrollment() {
		return false
	}

	// Download missing credential types/issuers/public keys from the scheme manager
	if session.downloaded, err = session.client.Configuration.Download(session.irmaSession.Identifiers()); err != nil {
		session.fail(&irma.SessionError{ErrorType: irma.ErrorConfigurationDownload, Err: err})
		return false
	}
	return true
}

Sietse Ringers's avatar
Sietse Ringers committed
219
// NewManualSession starts a manual session, given a signature request in JSON and a handler to pass messages to
Koen van Ingen's avatar
Koen van Ingen committed
220
func (client *Client) NewManualSession(sigrequestJSONString string, handler Handler) {
Sietse Ringers's avatar
Sietse Ringers committed
221
222
223
	var err error
	sigrequest := &irma.SignatureRequest{}
	if err = json.Unmarshal([]byte(sigrequestJSONString), sigrequest); err != nil {
Koen van Ingen's avatar
Koen van Ingen committed
224
225
226
227
		handler.Failure(irma.ActionUnknown, &irma.SessionError{Err: err})
		return
	}

228
229
230
231
	session := &session{
		Action:      irma.ActionSigning, // TODO hardcoded for now
		Handler:     handler,
		client:      client,
232
		Version:     irma.NewVersion(2, 0), // TODO hardcoded for now
233
		irmaSession: sigrequest,
Koen van Ingen's avatar
Koen van Ingen committed
234
235
236
237
	}

	session.Handler.StatusUpdate(session.Action, irma.StatusManualStarted)

238
	if !session.checkAndUpateConfiguration() {
239
240
241
		return
	}

242
	candidates, missing := session.client.CheckSatisfiability(session.irmaSession.ToDisclose())
243
	if len(missing) > 0 {
244
		session.Handler.UnsatisfiableRequest(session.Action, "E-mail request", missing)
245
246
247
		// TODO: session.transport.Delete() on dialog cancel
		return
	}
248
	session.irmaSession.SetCandidates(candidates)
249
250
251
252

	// Ask for permission to execute the session
	callback := PermissionHandler(func(proceed bool, choice *irma.DisclosureChoice) {
		session.choice = choice
253
		session.irmaSession.SetDisclosureChoice(choice)
Koen van Ingen's avatar
Koen van Ingen committed
254
		go session.do(proceed)
255
256
	})
	session.Handler.RequestSignaturePermission(
Koen van Ingen's avatar
Koen van Ingen committed
257
		*session.irmaSession.(*irma.SignatureRequest), "E-mail request", callback)
258
259
}

260
// NewSession creates and starts a new interactive IRMA session
261
func (client *Client) NewSession(qr *irma.Qr, handler Handler) SessionDismisser {
262
	session := &session{
263
		ServerURL: qr.URL,
264
		transport: irma.NewHTTPTransport(qr.URL),
265
266
267
		Action:    irma.Action(qr.Type),
		Handler:   handler,
		client:    client,
268
	}
269
270
271
272
273
274

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

275
276
	version, err := calcVersion(qr)
	if err != nil {
277
		session.fail(&irma.SessionError{ErrorType: irma.ErrorProtocolVersionNotSupported, Err: err})
278
		return nil
279
	}
280
281
	session.Version = version
	session.transport.SetHeader("X-IRMA-ProtocolVersion", version.String())
282
283
284

	// Check if the action is one of the supported types
	switch session.Action {
285
286
287
288
	case irma.ActionDisclosing: // nop
	case irma.ActionSigning: // nop
	case irma.ActionIssuing: // nop
	case irma.ActionUnknown:
289
290
		fallthrough
	default:
291
		session.fail(&irma.SessionError{ErrorType: irma.ErrorUnknownAction, Info: string(session.Action)})
292
		return nil
293
294
295
296
297
298
299
300
	}

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

	go session.start()

301
	return session
302
303
}

304
305
// start retrieves the first message in the IRMA protocol, checks if we can perform
// the request, and informs the user of the outcome.
306
func (session *session) start() {
Koen van Ingen's avatar
Koen van Ingen committed
307
	defer session.panicFailure()
308

309
	session.Handler.StatusUpdate(session.Action, irma.StatusCommunicating)
310

Sietse Ringers's avatar
Sietse Ringers committed
311
	// Get the first IRMA protocol message and parse it
312
	session.info = &irma.SessionInfo{}
Sietse Ringers's avatar
Sietse Ringers committed
313
	Err := session.transport.Get("jwt", session.info)
Sietse Ringers's avatar
Sietse Ringers committed
314
	if Err != nil {
315
		session.fail(Err.(*irma.SessionError))
316
317
318
		return
	}

Sietse Ringers's avatar
Sietse Ringers committed
319
	var err error
320
	session.jwt, err = irma.ParseRequestorJwt(session.Action, session.info.Jwt)
Sietse Ringers's avatar
Sietse Ringers committed
321
	if err != nil {
322
		session.fail(&irma.SessionError{ErrorType: irma.ErrorInvalidJWT, Err: err})
Sietse Ringers's avatar
Sietse Ringers committed
323
324
		return
	}
Sietse Ringers's avatar
Sietse Ringers committed
325
	session.irmaSession = session.jwt.IrmaSession()
Sietse Ringers's avatar
Sietse Ringers committed
326
327
	session.irmaSession.SetContext(session.info.Context)
	session.irmaSession.SetNonce(session.info.Nonce)
328
	session.irmaSession.SetVersion(session.Version)
329
330
	if session.Action == irma.ActionIssuing {
		ir := session.irmaSession.(*irma.IssuanceRequest)
331
		// Store which public keys the server will use
332
		for _, credreq := range ir.Credentials {
333
			credreq.KeyCounter = session.info.Keys[credreq.CredentialTypeID.IssuerIdentifier()]
334
335
336
		}
	}

337
	if !session.checkAndUpateConfiguration() {
338
339
340
		return
	}

341
342
	if session.Action == irma.ActionIssuing {
		ir := session.irmaSession.(*irma.IssuanceRequest)
Sietse Ringers's avatar
Sietse Ringers committed
343
		for _, credreq := range ir.Credentials {
344
			info, err := credreq.Info(session.client.Configuration, getMetadataVersion(session.Version))
345
			if err != nil {
346
				session.fail(&irma.SessionError{ErrorType: irma.ErrorUnknownCredentialType, Err: err})
347
348
349
				return
			}
			ir.CredentialInfoList = append(ir.CredentialInfoList, info)
350
351
352
		}
	}

353
	candidates, missing := session.client.CheckSatisfiability(session.irmaSession.ToDisclose())
354
	if len(missing) > 0 {
355
		session.Handler.UnsatisfiableRequest(session.Action, session.jwt.Requestor(), missing)
356
		// TODO: session.transport.Delete() on dialog cancel
357
358
		return
	}
359
	session.irmaSession.SetCandidates(candidates)
360

Sietse Ringers's avatar
Sietse Ringers committed
361
	// Ask for permission to execute the session
362
	callback := PermissionHandler(func(proceed bool, choice *irma.DisclosureChoice) {
Sietse Ringers's avatar
Sietse Ringers committed
363
364
365
		session.choice = choice
		session.irmaSession.SetDisclosureChoice(choice)
		go session.do(proceed)
366
	})
367
	session.Handler.StatusUpdate(session.Action, irma.StatusConnected)
368
	switch session.Action {
369
	case irma.ActionDisclosing:
370
		session.Handler.RequestVerificationPermission(
371
372
			*session.irmaSession.(*irma.DisclosureRequest), session.jwt.Requestor(), callback)
	case irma.ActionSigning:
373
		session.Handler.RequestSignaturePermission(
374
375
			*session.irmaSession.(*irma.SignatureRequest), session.jwt.Requestor(), callback)
	case irma.ActionIssuing:
376
		session.Handler.RequestIssuancePermission(
377
			*session.irmaSession.(*irma.IssuanceRequest), session.jwt.Requestor(), callback)
378
379
380
381
382
	default:
		panic("Invalid session type") // does not happen, session.Action has been checked earlier
	}
}

383
func (session *session) do(proceed bool) {
Koen van Ingen's avatar
Koen van Ingen committed
384
	defer session.panicFailure()
385

386
	if !proceed {
387
		session.cancel()
388
389
		return
	}
390
	session.Handler.StatusUpdate(session.Action, irma.StatusCommunicating)
391

392
	if !session.Distributed() {
Koen van Ingen's avatar
Koen van Ingen committed
393
		message, err := session.getProof()
Sietse Ringers's avatar
Sietse Ringers committed
394
		if err != nil {
395
			session.fail(&irma.SessionError{ErrorType: irma.ErrorCrypto, Err: err})
Sietse Ringers's avatar
Sietse Ringers committed
396
397
398
399
			return
		}
		session.sendResponse(message)
	} else {
Koen van Ingen's avatar
Koen van Ingen committed
400
		builders, err := session.getBuilders()
Sietse Ringers's avatar
Sietse Ringers committed
401
		if err != nil {
402
			session.fail(&irma.SessionError{ErrorType: irma.ErrorCrypto, Err: err})
Sietse Ringers's avatar
Sietse Ringers committed
403
		}
404
405
406
407
408
		startKeyshareSession(
			session,
			session.Handler,
			builders,
			session.irmaSession,
409
			session.client.Configuration,
410
			session.client.keyshareServers,
411
			session.client.state,
412
		)
413
	}
Sietse Ringers's avatar
Sietse Ringers committed
414
}
415

416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
func (session *session) Distributed() bool {
	var smi irma.SchemeManagerIdentifier
	if session.Action == irma.ActionIssuing {
		for _, credreq := range session.irmaSession.(*irma.IssuanceRequest).Credentials {
			smi = credreq.CredentialTypeID.IssuerIdentifier().SchemeManagerIdentifier()
			if session.client.Configuration.SchemeManagers[smi].Distributed() {
				return true
			}
		}
	}

	if session.choice == nil || session.choice.Attributes == nil {
		return false
	}

	for _, ai := range session.choice.Attributes {
		smi = ai.Type.CredentialTypeIdentifier().IssuerIdentifier().SchemeManagerIdentifier()
		if session.client.Configuration.SchemeManagers[smi].Distributed() {
			return true
		}
	}

	return false
}

441
func (session *session) KeyshareDone(message interface{}) {
Sietse Ringers's avatar
Sietse Ringers committed
442
443
444
	session.sendResponse(message)
}

445
func (session *session) KeyshareCancelled() {
446
	session.cancel()
Sietse Ringers's avatar
Sietse Ringers committed
447
448
}

Sietse Ringers's avatar
Sietse Ringers committed
449
func (session *session) KeyshareEnrollmentIncomplete(manager irma.SchemeManagerIdentifier) {
450
	session.Handler.KeyshareEnrollmentIncomplete(manager)
Sietse Ringers's avatar
Sietse Ringers committed
451
452
}

453
func (session *session) KeyshareBlocked(manager irma.SchemeManagerIdentifier, duration int) {
454
	session.Handler.KeyshareBlocked(manager, duration)
Sietse Ringers's avatar
Sietse Ringers committed
455
456
}

457
func (session *session) KeyshareError(manager *irma.SchemeManagerIdentifier, 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
}

Koen van Ingen's avatar
Koen van Ingen committed
468
func (session *session) KeysharePin() {
469
470
	session.Handler.StatusUpdate(session.Action, irma.StatusConnected)
}
Sietse Ringers's avatar
Sietse Ringers committed
471

Koen van Ingen's avatar
Koen van Ingen committed
472
func (session *session) KeysharePinOK() {
473
474
	session.Handler.StatusUpdate(session.Action, irma.StatusCommunicating)
}
Sietse Ringers's avatar
Sietse Ringers committed
475

Sietse Ringers's avatar
Sietse Ringers committed
476
477
type disclosureResponse string

478
func (session *session) sendResponse(message interface{}) {
Sietse Ringers's avatar
Sietse Ringers committed
479
	var log *LogEntry
Sietse Ringers's avatar
Sietse Ringers committed
480
	var err error
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
	var messageJson []byte

	if session.IsInteractive() {
		switch session.Action {
		case irma.ActionSigning:
			fallthrough
		case irma.ActionDisclosing:
			var response disclosureResponse
			if err = session.transport.Post("proofs", &response, message); err != nil {
				session.fail(err.(*irma.SessionError))
				return
			}
			if response != "VALID" {
				session.fail(&irma.SessionError{ErrorType: irma.ErrorRejected, Info: string(response)})
				return
			}
			log, _ = session.createLogEntry(message.(gabi.ProofList)) // TODO err
		case irma.ActionIssuing:
			response := []*gabi.IssueSignatureMessage{}
			if err = session.transport.Post("commitments", &response, message); err != nil {
				session.fail(err.(*irma.SessionError))
				return
			}
			if err = session.client.ConstructCredentials(response, session.irmaSession.(*irma.IssuanceRequest)); err != nil {
				session.fail(&irma.SessionError{ErrorType: irma.ErrorCrypto, Err: err})
				return
			}
			log, _ = session.createLogEntry(message) // TODO err
Sietse Ringers's avatar
Sietse Ringers committed
509
		}
510
511
512
513
	} else {
		messageJson, err = json.Marshal(message)
		if err != nil {
			session.fail(&irma.SessionError{ErrorType: irma.ErrorSerialization, Err: err})
Sietse Ringers's avatar
Sietse Ringers committed
514
515
			return
		}
516
517
	}

518
	_ = session.client.addLogEntry(log) // TODO err
519
	if !session.downloaded.Empty() {
520
		session.client.handler.UpdateConfiguration(session.downloaded)
521
	}
522
	if session.Action == irma.ActionIssuing {
523
		session.client.handler.UpdateAttributes()
524
	}
525
	session.done = true
526
527
528
	session.Handler.Success(session.Action, string(messageJson))
}

529
func (session *session) managerSession() {
530
	defer func() {
531
		if e := recover(); e != nil {
532
			if session.Handler != nil {
533
				session.Handler.Failure(session.Action, panicToError(e))
534
			}
535
		}
536
537
538
539
540
	}()

	// 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.
541
	manager, err := irma.DownloadSchemeManager(session.ServerURL)
542
	if err != nil {
543
		session.Handler.Failure(session.Action, &irma.SessionError{ErrorType: irma.ErrorConfigurationDownload, Err: err})
544
545
		return
	}
546

547
	session.Handler.RequestSchemeManagerPermission(manager, func(proceed bool) {
548
		if !proceed {
549
			session.Handler.Cancelled(session.Action) // No need to DELETE session here
550
551
			return
		}
552
		if err := session.client.Configuration.InstallSchemeManager(manager); err != nil {
553
			session.Handler.Failure(session.Action, &irma.SessionError{ErrorType: irma.ErrorConfigurationDownload, Err: err})
Sietse Ringers's avatar
Sietse Ringers committed
554
555
			return
		}
556
557

		// Update state and inform user of success
558
		if manager.Distributed() {
559
			session.client.UnenrolledSchemeManagers = session.client.unenrolledSchemeManagers()
560
		}
561
		session.client.handler.UpdateConfiguration(
562
563
564
565
			&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
566
567
			},
		)
568
		session.Handler.Success(session.Action, "")
569
570
571
	})
	return
}
572
573
574

// Session lifetime functions

575
576
577
578
579
580
581
582
583
584
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
585
	}
586
	return &irma.SessionError{ErrorType: irma.ErrorPanic, Info: info}
587
588
589
}

// Idempotently send DELETE to remote server, returning whether or not we did something
590
func (session *session) delete() bool {
591
	if !session.done {
592
593
594
		if session.IsInteractive() {
			session.transport.Delete()
		}
595
		session.done = true
596
597
598
599
600
		return true
	}
	return false
}

601
func (session *session) fail(err *irma.SessionError) {
602
603
604
	if session.delete() {
		err.Err = errors.Wrap(err.Err, 0)
		if session.downloaded != nil && !session.downloaded.Empty() {
605
			session.client.handler.UpdateConfiguration(session.downloaded)
606
607
608
609
610
		}
		session.Handler.Failure(session.Action, err)
	}
}

611
func (session *session) cancel() {
612
613
	if session.delete() {
		if session.downloaded != nil && !session.downloaded.Empty() {
614
			session.client.handler.UpdateConfiguration(session.downloaded)
615
616
617
618
619
		}
		session.Handler.Cancelled(session.Action)
	}
}

620
func (session *session) Dismiss() {
621
622
	session.cancel()
}
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638

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
}