manager.go 21.2 KB
Newer Older
1
2
3
package irmago

import (
4
	"crypto/rand"
Sietse Ringers's avatar
Sietse Ringers committed
5
	"math/big"
Sietse Ringers's avatar
Sietse Ringers committed
6
	"sort"
7
	"time"
Sietse Ringers's avatar
Sietse Ringers committed
8

9
	"github.com/credentials/go-go-gadget-paillier"
Sietse Ringers's avatar
Sietse Ringers committed
10
	"github.com/go-errors/errors"
11
12
13
	"github.com/mhe/gabi"
)

Sietse Ringers's avatar
Sietse Ringers committed
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// This file contains most methods of the CredentialManager (c.f. session.go
// and updates.go).
//
// The storage of credentials is split up in several parts:
//
// - The CL-signature of each credential is stored separately, so that we can
// load it on demand (i.e., during an IRMA session), instead of immediately
// at initialization.
//
// - The attributes of all credentials are stored together, as they all
// immediately need to be available anyway,
//
// - The secret key (the zeroth attribute of every credential), being the same
// across all credentials, is stored only once in a separate file (storing this
// in multiple places would be bad).

// CredentialManager (de)serializes credentials and keyshare server information
// from storage; as well as logs of earlier IRMA sessions; it provides access
// to the attributes and all related information of its credentials;
// it is the starting point for new IRMA sessions; and it computes some
// of the messages in the client side of the IRMA protocol.
35
type CredentialManager struct {
Sietse Ringers's avatar
Sietse Ringers committed
36
	// Stuff we manage on disk
37
	secretkey        *secretKey
38
39
40
	attributes       map[CredentialTypeIdentifier][]*AttributeList
	credentials      map[CredentialTypeIdentifier]map[int]*credential
	keyshareServers  map[SchemeManagerIdentifier]*keyshareServer
41
	paillierKeyCache *paillierPrivateKey
Sietse Ringers's avatar
Sietse Ringers committed
42
	logs             []*LogEntry
Sietse Ringers's avatar
Sietse Ringers committed
43
	updates          []update
44

Sietse Ringers's avatar
Sietse Ringers committed
45
46
47
48
	// Where we store/load it to/from
	storage storage

	// Other state
49
50
51
52
53
	ConfigurationStore       *ConfigurationStore
	UnenrolledSchemeManagers []SchemeManagerIdentifier
	irmaConfigurationPath    string
	androidStoragePath       string
	handler                  ClientHandler
54
55
56
}

// KeyshareHandler is used for asking the user for his email address and PIN,
57
// for enrolling at a keyshare server.
58
type KeyshareHandler interface {
59
60
	EnrollmentError(manager SchemeManagerIdentifier, err error)
	EnrollmentSuccess(manager SchemeManagerIdentifier)
61
62
63
64
65
66
67
}

type ClientHandler interface {
	KeyshareHandler

	UpdateConfigurationStore(new *IrmaIdentifierSet)
	UpdateAttributes()
Sietse Ringers's avatar
Sietse Ringers committed
68
69
}

70
71
72
73
type secretKey struct {
	Key *big.Int
}

74
75
76
77
78
// NewCredentialManager creates a new CredentialManager that uses the directory
// specified by storagePath for (de)serializing itself. irmaConfigurationPath
// is the path to a (possibly readonly) folder containing irma_configuration;
// androidStoragePath is an optional path to the files of the old android app
// (specify "" if you do not want to parse the old android app files),
79
// and handler is used for informing the user of new stuff, and when a
80
// enrollment to a keyshare server needs to happen.
81
82
83
// The credential manager returned by this function has been fully deserialized
// and is ready for use.
//
Sietse Ringers's avatar
Sietse Ringers committed
84
85
// NOTE: It is the responsibility of the caller that there exists a (properly
// protected) directory at storagePath!
86
87
88
89
func NewCredentialManager(
	storagePath string,
	irmaConfigurationPath string,
	androidStoragePath string,
90
	handler ClientHandler,
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
) (*CredentialManager, error) {
	var err error
	if err = AssertPathExists(storagePath); err != nil {
		return nil, err
	}
	if err = AssertPathExists(irmaConfigurationPath); err != nil {
		return nil, err
	}

	cm := &CredentialManager{
		credentials:           make(map[CredentialTypeIdentifier]map[int]*credential),
		keyshareServers:       make(map[SchemeManagerIdentifier]*keyshareServer),
		attributes:            make(map[CredentialTypeIdentifier][]*AttributeList),
		irmaConfigurationPath: irmaConfigurationPath,
		androidStoragePath:    androidStoragePath,
106
		handler:               handler,
107
108
109
110
111
112
113
114
	}

	cm.ConfigurationStore, err = NewConfigurationStore(storagePath+"/irma_configuration", irmaConfigurationPath)
	if err != nil {
		return nil, err
	}
	if err = cm.ConfigurationStore.ParseFolder(); err != nil {
		return nil, err
115
116
117
	}

	// Ensure storage path exists, and populate it with necessary files
118
	cm.storage = storage{storagePath: storagePath, ConfigurationStore: cm.ConfigurationStore}
Sietse Ringers's avatar
Sietse Ringers committed
119
	if err = cm.storage.EnsureStorageExists(); err != nil {
120
121
122
123
124
125
126
127
128
		return nil, err
	}

	// Perform new update functions from credentialManagerUpdates, if any
	if err = cm.update(); err != nil {
		return nil, err
	}

	// Load our stuff
Sietse Ringers's avatar
Sietse Ringers committed
129
	if cm.secretkey, err = cm.storage.LoadSecretKey(); err != nil {
130
131
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
132
	if cm.attributes, err = cm.storage.LoadAttributes(); err != nil {
133
134
		return nil, err
	}
135
	if cm.keyshareServers, err = cm.storage.LoadKeyshareServers(); err != nil {
136
137
		return nil, err
	}
138
	if cm.paillierKeyCache, err = cm.storage.LoadPaillierKeys(); err != nil {
139
140
		return nil, err
	}
141
142
143
	if cm.paillierKeyCache == nil {
		cm.paillierKey(false)
	}
144

145
146
	cm.UnenrolledSchemeManagers = cm.unenrolledSchemeManagers()
	if len(cm.UnenrolledSchemeManagers) > 1 {
147
148
149
150
151
152
		return nil, errors.New("Too many keyshare servers")
	}

	return cm, nil
}

153
154
155
// CredentialInfoList returns a list of information of all contained credentials.
func (cm *CredentialManager) CredentialInfoList() CredentialInfoList {
	list := CredentialInfoList([]*CredentialInfo{})
156
157

	for _, attrlistlist := range cm.attributes {
158
159
160
		for index, attrlist := range attrlistlist {
			info := attrlist.Info()
			info.Index = index
161
			list = append(list, info)
Sietse Ringers's avatar
Sietse Ringers committed
162
163
		}
	}
164

Sietse Ringers's avatar
Sietse Ringers committed
165
166
167
168
	sort.Sort(list)
	return list
}

Sietse Ringers's avatar
Sietse Ringers committed
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
// addCredential adds the specified credential to the CredentialManager, saving its signature
// imediately, and optionally cm.attributes as well.
func (cm *CredentialManager) addCredential(cred *credential, storeAttributes bool) (err error) {
	id := cred.CredentialType().Identifier()
	cm.attributes[id] = append(cm.attrs(id), cred.AttributeList())

	if _, exists := cm.credentials[id]; !exists {
		cm.credentials[id] = make(map[int]*credential)
	}
	if cred.CredentialType().IsSingleton {
		cm.credentials[id][0] = cred
	} else {
		counter := len(cm.attributes[id]) - 1
		cm.credentials[id][counter] = cred
	}

	if err = cm.storage.StoreSignature(cred); err != nil {
		return
	}
	if storeAttributes {
		err = cm.storage.StoreAttributes(cm.attributes)
	}
	return
}

func generateSecretKey() (*secretKey, error) {
	key, err := gabi.RandomBigInt(gabi.DefaultSystemParameters[1024].Lm)
	if err != nil {
		return nil, err
	}
	return &secretKey{Key: key}, nil
}

// Removal methods

204
205
206
207
208
209
210
211
212
func (cm *CredentialManager) remove(id CredentialTypeIdentifier, index int, storenow bool) error {
	// Remove attributes
	list, exists := cm.attributes[id]
	if !exists || index >= len(list) {
		return errors.Errorf("Can't remove credential %s-%d: no such credential", id.String(), index)
	}
	attrs := list[index]
	cm.attributes[id] = append(list[:index], list[index+1:]...)
	if storenow {
Sietse Ringers's avatar
Sietse Ringers committed
213
214
215
		if err := cm.storage.StoreAttributes(cm.attributes); err != nil {
			return err
		}
216
217
218
219
220
	}

	// Remove credential
	if creds, exists := cm.credentials[id]; exists {
		if _, exists := creds[index]; exists {
Sietse Ringers's avatar
Sietse Ringers committed
221
			delete(creds, index)
222
223
224
225
226
			cm.credentials[id] = creds
		}
	}

	// Remove signature from storage
Sietse Ringers's avatar
Sietse Ringers committed
227
	if err := cm.storage.DeleteSignature(attrs); err != nil {
228
229
230
		return err
	}

231
232
233
234
235
236
237
238
239
240
241
	removed := map[CredentialTypeIdentifier][]TranslatedString{}
	removed[id] = attrs.Strings()

	if storenow {
		return cm.addLogEntry(&LogEntry{
			Type:    actionRemoval,
			Time:    Timestamp(time.Now()),
			Removed: removed,
		})
	}
	return nil
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
}

func (cm *CredentialManager) RemoveCredential(id CredentialTypeIdentifier, index int) error {
	return cm.remove(id, index, true)
}

func (cm *CredentialManager) RemoveCredentialByHash(hash string) error {
	cred, index, err := cm.credentialByHash(hash)
	if err != nil {
		return err
	}
	return cm.RemoveCredential(cred.CredentialType().Identifier(), index)
}

func (cm *CredentialManager) RemoveAllCredentials() error {
257
	removed := map[CredentialTypeIdentifier][]TranslatedString{}
Sietse Ringers's avatar
Sietse Ringers committed
258
259
260
261
262
263
	for _, attrlistlist := range cm.attributes {
		for _, attrs := range attrlistlist {
			if attrs.CredentialType() != nil {
				removed[attrs.CredentialType().Identifier()] = attrs.Strings()
			}
			cm.storage.DeleteSignature(attrs)
264
265
		}
	}
Sietse Ringers's avatar
Sietse Ringers committed
266
	cm.attributes = map[CredentialTypeIdentifier][]*AttributeList{}
Sietse Ringers's avatar
Sietse Ringers committed
267
	if err := cm.storage.StoreAttributes(cm.attributes); err != nil {
268
269
		return err
	}
270
271
272
273
274
275
276
277
278

	logentry := &LogEntry{
		Type:    actionRemoval,
		Time:    Timestamp(time.Now()),
		Removed: removed,
	}
	if err := cm.addLogEntry(logentry); err != nil {
		return err
	}
Sietse Ringers's avatar
Sietse Ringers committed
279
	return cm.storage.StoreLogs(cm.logs)
280
281
}

282
// Attribute and credential getter methods
Sietse Ringers's avatar
Sietse Ringers committed
283

Sietse Ringers's avatar
Sietse Ringers committed
284
// attrs returns cm.attributes[id], initializing it to an empty slice if neccesary
285
func (cm *CredentialManager) attrs(id CredentialTypeIdentifier) []*AttributeList {
Sietse Ringers's avatar
Sietse Ringers committed
286
287
288
289
290
291
292
293
294
	list, exists := cm.attributes[id]
	if !exists {
		list = make([]*AttributeList, 0, 1)
		cm.attributes[id] = list
	}
	return list
}

// creds returns cm.credentials[id], initializing it to an empty map if neccesary
Sietse Ringers's avatar
Sietse Ringers committed
295
func (cm *CredentialManager) creds(id CredentialTypeIdentifier) map[int]*credential {
Sietse Ringers's avatar
Sietse Ringers committed
296
297
	list, exists := cm.credentials[id]
	if !exists {
Sietse Ringers's avatar
Sietse Ringers committed
298
		list = make(map[int]*credential)
Sietse Ringers's avatar
Sietse Ringers committed
299
300
301
302
303
		cm.credentials[id] = list
	}
	return list
}

Sietse Ringers's avatar
Sietse Ringers committed
304
// Attributes returns the attribute list of the requested credential, or nil if we do not have it.
305
func (cm *CredentialManager) Attributes(id CredentialTypeIdentifier, counter int) (attributes *AttributeList) {
Sietse Ringers's avatar
Sietse Ringers committed
306
307
	list := cm.attrs(id)
	if len(list) <= counter {
Sietse Ringers's avatar
Sietse Ringers committed
308
309
310
311
312
		return
	}
	return list[counter]
}

313
314
315
316
317
318
319
320
321
322
323
324
325
func (cm *CredentialManager) credentialByHash(hash string) (*credential, int, error) {
	for _, attrlistlist := range cm.attributes {
		for index, attrs := range attrlistlist {
			if attrs.hash() == hash {
				cred, err := cm.credential(attrs.CredentialType().Identifier(), index)
				return cred, index, err
			}
		}
	}
	return nil, 0, nil
}

func (cm *CredentialManager) credentialByID(id CredentialIdentifier) (*credential, error) {
Sietse Ringers's avatar
Sietse Ringers committed
326
327
328
329
330
331
332
333
334
	if _, exists := cm.attributes[id.Type]; !exists {
		return nil, nil
	}
	for index, attrs := range cm.attributes[id.Type] {
		if attrs.hash() == id.Hash {
			return cm.credential(attrs.CredentialType().Identifier(), index)
		}
	}
	return nil, nil
335
336
}

Sietse Ringers's avatar
Sietse Ringers committed
337
338
// credential returns the requested credential, or nil if we do not have it.
func (cm *CredentialManager) credential(id CredentialTypeIdentifier, counter int) (cred *credential, err error) {
Sietse Ringers's avatar
Sietse Ringers committed
339
	// If the requested credential is not in credential map, we check if its attributes were
340
	// deserialized during NewCredentialManager(). If so, there should be a corresponding signature file,
Sietse Ringers's avatar
Sietse Ringers committed
341
	// so we read that, construct the credential, and add it to the credential map
Sietse Ringers's avatar
Sietse Ringers committed
342
	if _, exists := cm.creds(id)[counter]; !exists {
Sietse Ringers's avatar
Sietse Ringers committed
343
344
345
346
		attrs := cm.Attributes(id, counter)
		if attrs == nil { // We do not have the requested cred
			return
		}
Sietse Ringers's avatar
Sietse Ringers committed
347
		sig, err := cm.storage.LoadSignature(attrs)
Sietse Ringers's avatar
Sietse Ringers committed
348
349
350
351
352
353
354
		if err != nil {
			return nil, err
		}
		if sig == nil {
			err = errors.New("signature file not found")
			return nil, err
		}
355
		pk, err := attrs.PublicKey()
356
357
358
		if err != nil {
			return nil, err
		}
359
360
361
		if pk == nil {
			return nil, errors.New("unknown public key")
		}
362
		cred, err := newCredential(&gabi.Credential{
363
			Attributes: append([]*big.Int{cm.secretkey.Key}, attrs.Ints...),
364
			Signature:  sig,
365
			Pk:         pk,
366
		}, cm.ConfigurationStore)
367
368
369
		if err != nil {
			return nil, err
		}
Sietse Ringers's avatar
Sietse Ringers committed
370
371
372
373
		cm.credentials[id][counter] = cred
	}

	return cm.credentials[id][counter], nil
374
375
}

Sietse Ringers's avatar
Sietse Ringers committed
376
// Methods used in the IRMA protocol
377

Sietse Ringers's avatar
Sietse Ringers committed
378
379
// Candidates returns a list of attributes present in this credential manager
// that satisfy the specified attribute disjunction.
380
func (cm *CredentialManager) Candidates(disjunction *AttributeDisjunction) []*AttributeIdentifier {
381
	candidates := make([]*AttributeIdentifier, 0, 10)
382
383

	for _, attribute := range disjunction.Attributes {
Sietse Ringers's avatar
Sietse Ringers committed
384
		credID := attribute.CredentialTypeIdentifier()
385
		if !cm.ConfigurationStore.Contains(credID) {
386
387
			continue
		}
388
		creds := cm.attributes[credID]
389
390
391
392
		count := len(creds)
		if count == 0 {
			continue
		}
393
394
		for _, attrs := range creds {
			id := &AttributeIdentifier{Type: attribute, Hash: attrs.hash()}
395
396
397
			if attribute.IsCredential() {
				candidates = append(candidates, id)
			} else {
Sietse Ringers's avatar
Sietse Ringers committed
398
				val := attrs.untranslatedAttribute(attribute)
399
				if val == "" { // This won't handle empty attributes correctly
400
401
402
403
404
405
406
407
408
409
410
411
					continue
				}
				if !disjunction.HasValues() || val == disjunction.Values[attribute] {
					candidates = append(candidates, id)
				}
			}
		}
	}

	return candidates
}

Sietse Ringers's avatar
Sietse Ringers committed
412
413
414
// CheckSatisfiability checks if this credential manager has the required attributes
// to satisfy the specifed disjunction list. If not, the unsatisfiable disjunctions
// are returned.
415
416
417
418
419
420
421
422
423
func (cm *CredentialManager) CheckSatisfiability(
	disjunctions AttributeDisjunctionList,
) ([][]*AttributeIdentifier, AttributeDisjunctionList) {
	candidates := [][]*AttributeIdentifier{}
	missing := AttributeDisjunctionList{}
	for i, disjunction := range disjunctions {
		candidates = append(candidates, []*AttributeIdentifier{})
		candidates[i] = cm.Candidates(disjunction)
		if len(candidates[i]) == 0 {
424
425
426
			missing = append(missing, disjunction)
		}
	}
427
	return candidates, missing
428
}
429

430
func (cm *CredentialManager) groupCredentials(choice *DisclosureChoice) (map[CredentialIdentifier][]int, error) {
431
	grouped := make(map[CredentialIdentifier][]int)
432
433
434
	if choice == nil || choice.Attributes == nil {
		return grouped, nil
	}
435
436
437
438
439
440
441
442

	for _, attribute := range choice.Attributes {
		identifier := attribute.Type
		ici := attribute.CredentialIdentifier()

		// If this is the first attribute of its credential type that we encounter
		// in the disclosure choice, then there is no slice yet at grouped[ici]
		if _, present := grouped[ici]; !present {
443
444
			indices := make([]int, 1, 1)
			indices[0] = 1 // Always include metadata
445
446
447
448
449
450
			grouped[ici] = indices
		}

		if identifier.IsCredential() {
			continue // In this case we only disclose the metadata attribute, which is already handled
		}
451
		index, err := cm.ConfigurationStore.CredentialTypes[identifier.CredentialTypeIdentifier()].IndexOf(identifier)
452
453
454
455
		if err != nil {
			return nil, err
		}

Sietse Ringers's avatar
Sietse Ringers committed
456
		// These indices will be used in the []*big.Int at gabi.credential.Attributes,
457
		// which doesn't know about the secret key and metadata attribute, so +2
458
		grouped[ici] = append(grouped[ici], index+2)
459
460
461
462
463
	}

	return grouped, nil
}

464
// ProofBuilders constructs a list of proof builders for the specified attribute choice.
465
func (cm *CredentialManager) ProofBuilders(choice *DisclosureChoice) (gabi.ProofBuilderList, error) {
466
467
468
469
470
	todisclose, err := cm.groupCredentials(choice)
	if err != nil {
		return nil, err
	}

471
	builders := gabi.ProofBuilderList([]gabi.ProofBuilder{})
472
	for id, list := range todisclose {
Sietse Ringers's avatar
Sietse Ringers committed
473
		cred, err := cm.credentialByID(id)
474
475
476
477
478
		if err != nil {
			return nil, err
		}
		builders = append(builders, cred.Credential.CreateDisclosureProofBuilder(list))
	}
Sietse Ringers's avatar
Sietse Ringers committed
479
	return builders, nil
480
}
Sietse Ringers's avatar
Sietse Ringers committed
481

Sietse Ringers's avatar
Sietse Ringers committed
482
// Proofs computes disclosure proofs containing the attributes specified by choice.
483
func (cm *CredentialManager) Proofs(choice *DisclosureChoice, request IrmaSession, issig bool) (gabi.ProofList, error) {
Sietse Ringers's avatar
Sietse Ringers committed
484
	builders, err := cm.ProofBuilders(choice)
Sietse Ringers's avatar
Sietse Ringers committed
485
486
487
	if err != nil {
		return nil, err
	}
488
	return builders.BuildProofList(request.GetContext(), request.GetNonce(), issig), nil
Sietse Ringers's avatar
Sietse Ringers committed
489
490
}

491
492
// IssuanceProofBuilders constructs a list of proof builders in the issuance protocol
// for the future credentials as well as possibly any disclosed attributes.
493
func (cm *CredentialManager) IssuanceProofBuilders(request *IssuanceRequest) (gabi.ProofBuilderList, error) {
Sietse Ringers's avatar
Cleanup    
Sietse Ringers committed
494
	state, err := newIssuanceState()
Sietse Ringers's avatar
Sietse Ringers committed
495
496
497
498
499
	if err != nil {
		return nil, err
	}
	request.state = state

500
	proofBuilders := gabi.ProofBuilderList([]gabi.ProofBuilder{})
Sietse Ringers's avatar
Sietse Ringers committed
501
	for _, futurecred := range request.Credentials {
Sietse Ringers's avatar
Sietse Ringers committed
502
		var pk *gabi.PublicKey
503
		pk, err = cm.ConfigurationStore.PublicKey(futurecred.CredentialTypeID.IssuerIdentifier(), futurecred.KeyCounter)
504
505
506
		if err != nil {
			return nil, err
		}
507
508
		credBuilder := gabi.NewCredentialBuilder(
			pk, request.GetContext(), cm.secretkey.Key, state.nonce2)
Sietse Ringers's avatar
Sietse Ringers committed
509
510
511
		request.state.builders = append(request.state.builders, credBuilder)
		proofBuilders = append(proofBuilders, credBuilder)
	}
Sietse Ringers's avatar
Sietse Ringers committed
512

Sietse Ringers's avatar
Sietse Ringers committed
513
	disclosures, err := cm.ProofBuilders(request.choice)
Sietse Ringers's avatar
Sietse Ringers committed
514
515
516
	if err != nil {
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
517
	proofBuilders = append(disclosures, proofBuilders...)
Sietse Ringers's avatar
Sietse Ringers committed
518
519
	return proofBuilders, nil
}
Sietse Ringers's avatar
Sietse Ringers committed
520

Sietse Ringers's avatar
Sietse Ringers committed
521
522
523
524
525
526
527
// IssueCommitments computes issuance commitments, along with disclosure proofs
// specified by choice.
func (cm *CredentialManager) IssueCommitments(request *IssuanceRequest) (*gabi.IssueCommitmentMessage, error) {
	proofBuilders, err := cm.IssuanceProofBuilders(request)
	if err != nil {
		return nil, err
	}
528
	list := proofBuilders.BuildProofList(request.GetContext(), request.GetNonce(), false)
Sietse Ringers's avatar
Sietse Ringers committed
529
	return &gabi.IssueCommitmentMessage{Proofs: list, Nonce2: request.state.nonce2}, nil
Sietse Ringers's avatar
Sietse Ringers committed
530
531
}

Sietse Ringers's avatar
Sietse Ringers committed
532
533
// ConstructCredentials constructs and saves new credentials
// using the specified issuance signature messages.
Sietse Ringers's avatar
Sietse Ringers committed
534
535
536
537
538
func (cm *CredentialManager) ConstructCredentials(msg []*gabi.IssueSignatureMessage, request *IssuanceRequest) error {
	if len(msg) != len(request.state.builders) {
		return errors.New("Received unexpected amount of signatures")
	}

539
540
	// First collect all credentials in a slice, so that if one of them induces an error,
	// we save none of them to fail the session cleanly
541
	gabicreds := []*gabi.Credential{}
Sietse Ringers's avatar
Sietse Ringers committed
542
	for i, sig := range msg {
543
		attrs, err := request.Credentials[i].AttributeList(cm.ConfigurationStore)
Sietse Ringers's avatar
Sietse Ringers committed
544
545
546
547
548
549
550
		if err != nil {
			return err
		}
		cred, err := request.state.builders[i].ConstructCredential(sig, attrs.Ints)
		if err != nil {
			return err
		}
551
		gabicreds = append(gabicreds, cred)
Sietse Ringers's avatar
Sietse Ringers committed
552
553
	}

554
	for _, gabicred := range gabicreds {
555
		newcred, err := newCredential(gabicred, cm.ConfigurationStore)
556
557
558
		if err != nil {
			return err
		}
Sietse Ringers's avatar
Sietse Ringers committed
559
560
561
		if err = cm.addCredential(newcred, true); err != nil {
			return err
		}
Sietse Ringers's avatar
Sietse Ringers committed
562
	}
563

Sietse Ringers's avatar
Sietse Ringers committed
564
	return nil
Sietse Ringers's avatar
Sietse Ringers committed
565
}
566

Sietse Ringers's avatar
Sietse Ringers committed
567
568
// Keyshare server handling

569
// PaillierKey returns a new Paillier key (and generates a new one in a goroutine).
Sietse Ringers's avatar
Sietse Ringers committed
570
func (cm *CredentialManager) paillierKey(wait bool) *paillierPrivateKey {
Sietse Ringers's avatar
Sietse Ringers committed
571
	cached := cm.paillierKeyCache
Sietse Ringers's avatar
Sietse Ringers committed
572
	ch := make(chan bool)
573
574
575
576
577
578

	// Would just write cm.paillierKeyCache instead of cached here, but the worker
	// modifies cm.paillierKeyCache, and we must be sure that the boolean here and
	// the if-clause below match.
	go cm.paillierKeyWorker(cached == nil && wait, ch)
	if cached == nil && wait {
Sietse Ringers's avatar
Sietse Ringers committed
579
		<-ch
580
581
		// generate yet another one for future calls, but no need to wait now
		go cm.paillierKeyWorker(false, ch)
Sietse Ringers's avatar
Sietse Ringers committed
582
	}
Sietse Ringers's avatar
Sietse Ringers committed
583
	return cm.paillierKeyCache
584
}
Sietse Ringers's avatar
Sietse Ringers committed
585

586
587
588
589
590
591
592
593
594
func (cm *CredentialManager) paillierKeyWorker(wait bool, ch chan bool) {
	newkey, _ := paillier.GenerateKey(rand.Reader, 2048)
	cm.paillierKeyCache = (*paillierPrivateKey)(newkey)
	cm.storage.StorePaillierKeys(cm.paillierKeyCache)
	if wait {
		ch <- true
	}
}

595
func (cm *CredentialManager) unenrolledSchemeManagers() []SchemeManagerIdentifier {
Sietse Ringers's avatar
Sietse Ringers committed
596
	list := []SchemeManagerIdentifier{}
597
	for name, manager := range cm.ConfigurationStore.SchemeManagers {
Sietse Ringers's avatar
Sietse Ringers committed
598
599
		if _, contains := cm.keyshareServers[name]; manager.Distributed() && !contains {
			list = append(list, manager.Identifier())
Sietse Ringers's avatar
Sietse Ringers committed
600
601
602
603
		}
	}
	return list
}
Sietse Ringers's avatar
Sietse Ringers committed
604

605
// KeyshareEnroll attempts to enroll at the keyshare server of the specified scheme manager.
Sietse Ringers's avatar
Sietse Ringers committed
606
607
func (cm *CredentialManager) KeyshareEnroll(manager SchemeManagerIdentifier, email, pin string) {
	go func() {
608
609
610
611
612
613
614
615
		defer func() {
			handlePanic(func(err *SessionError) {
				if cm.handler != nil {
					cm.handler.EnrollmentError(manager, err)
				}
			})
		}()

Sietse Ringers's avatar
Sietse Ringers committed
616
		err := cm.keyshareEnrollWorker(manager, email, pin)
617
		cm.UnenrolledSchemeManagers = cm.unenrolledSchemeManagers()
Sietse Ringers's avatar
Sietse Ringers committed
618
		if err != nil {
619
			cm.handler.EnrollmentError(manager, err)
Sietse Ringers's avatar
Sietse Ringers committed
620
		} else {
621
			cm.handler.EnrollmentSuccess(manager)
Sietse Ringers's avatar
Sietse Ringers committed
622
623
		}
	}()
624

625
626
627
}

func (cm *CredentialManager) keyshareEnrollWorker(managerID SchemeManagerIdentifier, email, pin string) error {
628
	manager, ok := cm.ConfigurationStore.SchemeManagers[managerID]
Sietse Ringers's avatar
Sietse Ringers committed
629
630
631
632
633
634
635
636
637
638
639
	if !ok {
		return errors.New("Unknown scheme manager")
	}
	if len(manager.KeyshareServer) == 0 {
		return errors.New("Scheme manager has no keyshare server")
	}
	if len(pin) < 5 {
		return errors.New("PIN too short, must be at least 5 characters")
	}

	transport := NewHTTPTransport(manager.KeyshareServer)
640
	kss, err := newKeyshareServer(cm.paillierKey(true), manager.KeyshareServer, email)
Sietse Ringers's avatar
Sietse Ringers committed
641
642
643
	if err != nil {
		return err
	}
644
	message := keyshareEnrollment{
Sietse Ringers's avatar
Sietse Ringers committed
645
646
647
648
649
650
651
652
653
654
655
		Username:  email,
		Pin:       kss.HashedPin(pin),
		PublicKey: (*paillierPublicKey)(&kss.PrivateKey.PublicKey),
	}

	result := &struct{}{}
	err = transport.Post("web/users/selfenroll", result, message)
	if err != nil {
		return err
	}

656
	cm.keyshareServers[managerID] = kss
Sietse Ringers's avatar
Sietse Ringers committed
657
	return cm.storage.StoreKeyshareServers(cm.keyshareServers)
Sietse Ringers's avatar
Sietse Ringers committed
658
659
}

660
// KeyshareRemove unenrolls the keyshare server of the specified scheme manager.
Sietse Ringers's avatar
Sietse Ringers committed
661
662
663
664
665
func (cm *CredentialManager) KeyshareRemove(manager SchemeManagerIdentifier) error {
	if _, contains := cm.keyshareServers[manager]; !contains {
		return errors.New("Can't uninstall unknown keyshare server")
	}
	delete(cm.keyshareServers, manager)
Sietse Ringers's avatar
Sietse Ringers committed
666
	return cm.storage.StoreKeyshareServers(cm.keyshareServers)
Sietse Ringers's avatar
Sietse Ringers committed
667
}
Sietse Ringers's avatar
Sietse Ringers committed
668

669
670
func (cm *CredentialManager) KeyshareRemoveAll() error {
	cm.keyshareServers = map[SchemeManagerIdentifier]*keyshareServer{}
Tomas's avatar
Tomas committed
671
	cm.UnenrolledSchemeManagers = cm.unenrolledSchemeManagers()
672
673
674
	return cm.storage.StoreKeyshareServers(cm.keyshareServers)
}

Sietse Ringers's avatar
Sietse Ringers committed
675
676
// Add, load and store log entries

677
func (cm *CredentialManager) addLogEntry(entry *LogEntry) error {
Sietse Ringers's avatar
Sietse Ringers committed
678
	cm.logs = append(cm.logs, entry)
679
	return cm.storage.StoreLogs(cm.logs)
680
	return nil
Sietse Ringers's avatar
Sietse Ringers committed
681
682
683
684
685
}

func (cm *CredentialManager) Logs() ([]*LogEntry, error) {
	if cm.logs == nil || len(cm.logs) == 0 {
		var err error
Sietse Ringers's avatar
Sietse Ringers committed
686
		cm.logs, err = cm.storage.LoadLogs()
Sietse Ringers's avatar
Sietse Ringers committed
687
688
689
690
691
692
		if err != nil {
			return nil, err
		}
	}
	return cm.logs, nil
}