session.go 17.3 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 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
	StatusUpdate(action irma.Action, status irma.Status)
30
	Success(action irma.Action, result string)
31
32
33
34
35
36
37
38
39
	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
type session struct {
Koen van Ingen's avatar
Koen van Ingen committed
50
51
52
	Action  irma.Action
	Handler Handler
	Version irma.Version
53
54
55

	choice      *irma.DisclosureChoice
	client      *Client
Koen van Ingen's avatar
Koen van Ingen committed
56
	downloaded  *irma.IrmaIdentifierSet
57
	irmaSession irma.IrmaSession
58
	done        bool
59

60
	// These are empty on manual sessions
61
	ServerURL string
Koen van Ingen's avatar
Koen van Ingen committed
62
63
64
	info      *irma.SessionInfo
	jwt       irma.RequestorJwt
	transport *irma.HTTPTransport
65
66
}

67
// We implement the handler for the keyshare protocol
68
var _ keyshareSessionHandler = (*session)(nil)
69

70
71
// Supported protocol versions. Minor version numbers should be reverse sorted.
var supportedVersions = map[int][]int{
72
	2: {2, 1},
73
74
}

75
func calcVersion(qr *irma.Qr) (string, error) {
76
	// Parse range supported by server
77
78
79
80
81
82
83
84
85
86
87
88
	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 {
89
90
91
92
93
		return "", err
	}

	// Iterate supportedVersions in reverse sorted order (i.e. biggest major number first)
	keys := make([]int, 0, len(supportedVersions))
94
	for k := range supportedVersions {
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
		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)
}

110
111
112
113
func (session *session) IsInteractive() bool {
	return session.ServerURL != ""
}

Koen van Ingen's avatar
Koen van Ingen committed
114
115
116
117
118
119
120
121
122
123
124
125
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))
	}
126

Koen van Ingen's avatar
Koen van Ingen committed
127
	return builders, err
128
129
}

Koen van Ingen's avatar
Koen van Ingen committed
130
131
132
func (session *session) getProof() (interface{}, error) {
	var message interface{}
	var err error
133

Koen van Ingen's avatar
Koen van Ingen committed
134
135
136
137
138
139
140
141
	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))
	}
142

Koen van Ingen's avatar
Koen van Ingen committed
143
144
	return message, err
}
145

146
147
// 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
148
func (session *session) checkKeyshareEnrollment() bool {
149
	for id := range session.irmaSession.Identifiers().SchemeManagers {
150
151
152
		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
153
			return false
154
155
156
157
158
		}
		distributed := manager.Distributed()
		_, enrolled := session.client.keyshareServers[id]
		if distributed && !enrolled {
			session.Handler.MissingKeyshareEnrollment(id)
Koen van Ingen's avatar
Koen van Ingen committed
159
			return false
160
161
		}
	}
Koen van Ingen's avatar
Koen van Ingen committed
162
163
164
165
166
167
168
169
170
171
172
	return true
}

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

Sietse Ringers's avatar
Sietse Ringers committed
173
// 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
174
func (client *Client) NewManualSession(sigrequestJSONString string, handler Handler) {
Sietse Ringers's avatar
Sietse Ringers committed
175
176
177
	var err error
	sigrequest := &irma.SignatureRequest{}
	if err = json.Unmarshal([]byte(sigrequestJSONString), sigrequest); err != nil {
Koen van Ingen's avatar
Koen van Ingen committed
178
179
180
181
		handler.Failure(irma.ActionUnknown, &irma.SessionError{Err: err})
		return
	}

182
183
184
185
186
187
	session := &session{
		Action:      irma.ActionSigning, // TODO hardcoded for now
		Handler:     handler,
		client:      client,
		Version:     irma.Version("2"), // TODO hardcoded for now
		irmaSession: sigrequest,
Koen van Ingen's avatar
Koen van Ingen committed
188
189
190
191
192
193
194
195
	}

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

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

197
198
199
200
201
202
	// 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
	}

203
	candidates, missing := session.client.CheckSatisfiability(session.irmaSession.ToDisclose())
204
205
206
207
208
	if len(missing) > 0 {
		session.Handler.UnsatisfiableRequest(session.Action, missing)
		// TODO: session.transport.Delete() on dialog cancel
		return
	}
209
	session.irmaSession.SetCandidates(candidates)
210
211
212
213

	// Ask for permission to execute the session
	callback := PermissionHandler(func(proceed bool, choice *irma.DisclosureChoice) {
		session.choice = choice
214
		session.irmaSession.SetDisclosureChoice(choice)
Koen van Ingen's avatar
Koen van Ingen committed
215
		go session.do(proceed)
216
217
	})
	session.Handler.RequestSignaturePermission(
Koen van Ingen's avatar
Koen van Ingen committed
218
		*session.irmaSession.(*irma.SignatureRequest), "E-mail request", callback)
219
220
}

221
// NewSession creates and starts a new interactive IRMA session
222
func (client *Client) NewSession(qr *irma.Qr, handler Handler) SessionDismisser {
223
	session := &session{
224
		ServerURL: qr.URL,
225
		transport: irma.NewHTTPTransport(qr.URL),
226
227
228
		Action:    irma.Action(qr.Type),
		Handler:   handler,
		client:    client,
229
	}
230
231
232
233
234
235

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

236
237
	version, err := calcVersion(qr)
	if err != nil {
238
		session.fail(&irma.SessionError{ErrorType: irma.ErrorProtocolVersionNotSupported, Err: err})
239
		return nil
240
	}
241
	session.Version = irma.Version(version)
242
243
244

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

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

	go session.start()

261
	return session
262
263
}

264
265
// start retrieves the first message in the IRMA protocol, checks if we can perform
// the request, and informs the user of the outcome.
266
func (session *session) start() {
Koen van Ingen's avatar
Koen van Ingen committed
267
	defer session.panicFailure()
268

269
	session.Handler.StatusUpdate(session.Action, irma.StatusCommunicating)
270

Sietse Ringers's avatar
Sietse Ringers committed
271
	// Get the first IRMA protocol message and parse it
272
	session.info = &irma.SessionInfo{}
Sietse Ringers's avatar
Sietse Ringers committed
273
	Err := session.transport.Get("jwt", session.info)
Sietse Ringers's avatar
Sietse Ringers committed
274
	if Err != nil {
275
		session.fail(Err.(*irma.SessionError))
276
277
278
		return
	}

Sietse Ringers's avatar
Sietse Ringers committed
279
	var err error
280
	session.jwt, err = irma.ParseRequestorJwt(session.Action, session.info.Jwt)
Sietse Ringers's avatar
Sietse Ringers committed
281
	if err != nil {
282
		session.fail(&irma.SessionError{ErrorType: irma.ErrorInvalidJWT, Err: err})
Sietse Ringers's avatar
Sietse Ringers committed
283
284
		return
	}
Sietse Ringers's avatar
Sietse Ringers committed
285
	session.irmaSession = session.jwt.IrmaSession()
Sietse Ringers's avatar
Sietse Ringers committed
286
287
	session.irmaSession.SetContext(session.info.Context)
	session.irmaSession.SetNonce(session.info.Nonce)
288
289
	if session.Action == irma.ActionIssuing {
		ir := session.irmaSession.(*irma.IssuanceRequest)
290
		// Store which public keys the server will use
291
		for _, credreq := range ir.Credentials {
292
			credreq.KeyCounter = session.info.Keys[credreq.CredentialTypeID.IssuerIdentifier()]
293
294
295
		}
	}

296
	// Check if we are enrolled into all involved keyshare servers
Koen van Ingen's avatar
Koen van Ingen committed
297
298
	if !session.checkKeyshareEnrollment() {
		return
Sietse Ringers's avatar
Sietse Ringers committed
299
300
	}

Sietse Ringers's avatar
Sietse Ringers committed
301
	// Download missing credential types/issuers/public keys from the scheme manager
302
	if session.downloaded, err = session.client.Configuration.Download(session.irmaSession.Identifiers()); err != nil {
303
		session.fail(&irma.SessionError{ErrorType: irma.ErrorConfigurationDownload, Err: err})
304
305
306
		return
	}

307
308
	if session.Action == irma.ActionIssuing {
		ir := session.irmaSession.(*irma.IssuanceRequest)
Sietse Ringers's avatar
Sietse Ringers committed
309
		for _, credreq := range ir.Credentials {
310
			info, err := credreq.Info(session.client.Configuration)
311
			if err != nil {
312
				session.fail(&irma.SessionError{ErrorType: irma.ErrorUnknownCredentialType, Err: err})
313
314
315
				return
			}
			ir.CredentialInfoList = append(ir.CredentialInfoList, info)
316
317
318
		}
	}

319
	candidates, missing := session.client.CheckSatisfiability(session.irmaSession.ToDisclose())
320
321
	if len(missing) > 0 {
		session.Handler.UnsatisfiableRequest(session.Action, missing)
322
		// TODO: session.transport.Delete() on dialog cancel
323
324
		return
	}
325
	session.irmaSession.SetCandidates(candidates)
326

Sietse Ringers's avatar
Sietse Ringers committed
327
	// Ask for permission to execute the session
328
	callback := PermissionHandler(func(proceed bool, choice *irma.DisclosureChoice) {
Sietse Ringers's avatar
Sietse Ringers committed
329
330
331
		session.choice = choice
		session.irmaSession.SetDisclosureChoice(choice)
		go session.do(proceed)
332
	})
333
	session.Handler.StatusUpdate(session.Action, irma.StatusConnected)
334
	switch session.Action {
335
	case irma.ActionDisclosing:
336
		session.Handler.RequestVerificationPermission(
337
338
			*session.irmaSession.(*irma.DisclosureRequest), session.jwt.Requestor(), callback)
	case irma.ActionSigning:
339
		session.Handler.RequestSignaturePermission(
340
341
			*session.irmaSession.(*irma.SignatureRequest), session.jwt.Requestor(), callback)
	case irma.ActionIssuing:
342
		session.Handler.RequestIssuancePermission(
343
			*session.irmaSession.(*irma.IssuanceRequest), session.jwt.Requestor(), callback)
344
345
346
347
348
	default:
		panic("Invalid session type") // does not happen, session.Action has been checked earlier
	}
}

349
func (session *session) do(proceed bool) {
Koen van Ingen's avatar
Koen van Ingen committed
350
	defer session.panicFailure()
351

352
	if !proceed {
353
		session.cancel()
354
355
		return
	}
356
	session.Handler.StatusUpdate(session.Action, irma.StatusCommunicating)
357

358
	if !session.irmaSession.Identifiers().Distributed(session.client.Configuration) {
Koen van Ingen's avatar
Koen van Ingen committed
359
		message, err := session.getProof()
Sietse Ringers's avatar
Sietse Ringers committed
360
		if err != nil {
361
			session.fail(&irma.SessionError{ErrorType: irma.ErrorCrypto, Err: err})
Sietse Ringers's avatar
Sietse Ringers committed
362
363
364
365
			return
		}
		session.sendResponse(message)
	} else {
Koen van Ingen's avatar
Koen van Ingen committed
366
		builders, err := session.getBuilders()
Sietse Ringers's avatar
Sietse Ringers committed
367
		if err != nil {
368
			session.fail(&irma.SessionError{ErrorType: irma.ErrorCrypto, Err: err})
Sietse Ringers's avatar
Sietse Ringers committed
369
		}
370
371
372
373
374
		startKeyshareSession(
			session,
			session.Handler,
			builders,
			session.irmaSession,
375
			session.client.Configuration,
376
			session.client.keyshareServers,
377
			session.client.state,
378
		)
379
	}
Sietse Ringers's avatar
Sietse Ringers committed
380
}
381

382
func (session *session) KeyshareDone(message interface{}) {
Sietse Ringers's avatar
Sietse Ringers committed
383
384
385
	session.sendResponse(message)
}

386
func (session *session) KeyshareCancelled() {
387
	session.cancel()
Sietse Ringers's avatar
Sietse Ringers committed
388
389
}

390
func (session *session) KeyshareBlocked(duration int) {
391
	session.fail(&irma.SessionError{ErrorType: irma.ErrorKeyshareBlocked, Info: strconv.Itoa(duration)})
Sietse Ringers's avatar
Sietse Ringers committed
392
393
}

394
func (session *session) KeyshareError(err error) {
395
396
397
398
399
400
401
402
	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
403
404
}

Koen van Ingen's avatar
Koen van Ingen committed
405
func (session *session) KeysharePin() {
406
407
	session.Handler.StatusUpdate(session.Action, irma.StatusConnected)
}
Sietse Ringers's avatar
Sietse Ringers committed
408

Koen van Ingen's avatar
Koen van Ingen committed
409
func (session *session) KeysharePinOK() {
410
411
	session.Handler.StatusUpdate(session.Action, irma.StatusCommunicating)
}
Sietse Ringers's avatar
Sietse Ringers committed
412

Sietse Ringers's avatar
Sietse Ringers committed
413
414
type disclosureResponse string

415
func (session *session) sendResponse(message interface{}) {
Sietse Ringers's avatar
Sietse Ringers committed
416
	var log *LogEntry
Sietse Ringers's avatar
Sietse Ringers committed
417
	var err error
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
	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
446
		}
447
448
449
450
	} 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
451
452
			return
		}
453
454
	}

455
	_ = session.client.addLogEntry(log) // TODO err
456
	if !session.downloaded.Empty() {
457
		session.client.handler.UpdateConfiguration(session.downloaded)
458
	}
459
	if session.Action == irma.ActionIssuing {
460
		session.client.handler.UpdateAttributes()
461
	}
462
	session.done = true
463
464
465
	session.Handler.Success(session.Action, string(messageJson))
}

466
func (session *session) managerSession() {
467
	defer func() {
468
		if e := recover(); e != nil {
469
			if session.Handler != nil {
470
				session.Handler.Failure(session.Action, panicToError(e))
471
			}
472
		}
473
474
475
476
477
	}()

	// 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.
478
	manager, err := session.client.Configuration.DownloadSchemeManager(session.ServerURL)
479
	if err != nil {
480
		session.Handler.Failure(session.Action, &irma.SessionError{ErrorType: irma.ErrorConfigurationDownload, Err: err})
481
482
		return
	}
483

484
	session.Handler.RequestSchemeManagerPermission(manager, func(proceed bool) {
485
		if !proceed {
486
			session.Handler.Cancelled(session.Action) // No need to DELETE session here
487
488
			return
		}
489
		if err := session.client.Configuration.AddSchemeManager(manager); err != nil {
490
			session.Handler.Failure(session.Action, &irma.SessionError{ErrorType: irma.ErrorConfigurationDownload, Err: err})
Sietse Ringers's avatar
Sietse Ringers committed
491
492
			return
		}
493
494

		// Update state and inform user of success
495
		if manager.Distributed() {
496
			session.client.UnenrolledSchemeManagers = session.client.unenrolledSchemeManagers()
497
		}
498
		session.client.handler.UpdateConfiguration(
499
500
501
502
			&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
503
504
			},
		)
505
		session.Handler.Success(session.Action, "")
506
507
508
	})
	return
}
509
510
511

// Session lifetime functions

512
513
514
515
516
517
518
519
520
521
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
522
	}
523
	return &irma.SessionError{ErrorType: irma.ErrorPanic, Info: info}
524
525
526
}

// Idempotently send DELETE to remote server, returning whether or not we did something
527
func (session *session) delete() bool {
528
	if !session.done {
529
530
531
		if session.IsInteractive() {
			session.transport.Delete()
		}
532
		session.done = true
533
534
535
536
537
		return true
	}
	return false
}

538
func (session *session) fail(err *irma.SessionError) {
539
540
541
	if session.delete() {
		err.Err = errors.Wrap(err.Err, 0)
		if session.downloaded != nil && !session.downloaded.Empty() {
542
			session.client.handler.UpdateConfiguration(session.downloaded)
543
544
545
546
547
		}
		session.Handler.Failure(session.Action, err)
	}
}

548
func (session *session) cancel() {
549
550
	if session.delete() {
		if session.downloaded != nil && !session.downloaded.Empty() {
551
			session.client.handler.UpdateConfiguration(session.downloaded)
552
553
554
555
556
		}
		session.Handler.Cancelled(session.Action)
	}
}

557
func (session *session) Dismiss() {
558
559
	session.cancel()
}
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575

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
}