manager.go 19.5 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
49
	// Where we store/load it to/from
	storage storage

	// Other state
	ConfigurationStore    *ConfigurationStore
50
	irmaConfigurationPath string
51
	androidStoragePath    string
Sietse Ringers's avatar
Sietse Ringers committed
52
53
}

54
55
56
57
type secretKey struct {
	Key *big.Int
}

Sietse Ringers's avatar
Sietse Ringers committed
58
59
60
61
62
63
// IrmaSession is an IRMA session.
type IrmaSession interface {
	GetNonce() *big.Int
	SetNonce(*big.Int)
	GetContext() *big.Int
	SetContext(*big.Int)
Sietse Ringers's avatar
Sietse Ringers committed
64
	ToDisclose() AttributeDisjunctionList
Sietse Ringers's avatar
Sietse Ringers committed
65
66
67
68
69
70
	DisclosureChoice() *DisclosureChoice
	SetDisclosureChoice(choice *DisclosureChoice)
	Distributed(store *ConfigurationStore) bool
	SchemeManagers() []SchemeManagerIdentifier
}

71
72
73
74
75
76
77
78
79
80
// 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),
// and keyshareHandler is used for when a registration to a keyshare server needs
// to happen.
// The credential manager returned by this function has been fully deserialized
// and is ready for use.
//
Sietse Ringers's avatar
Sietse Ringers committed
81
82
// NOTE: It is the responsibility of the caller that there exists a (properly
// protected) directory at storagePath!
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
func NewCredentialManager(
	storagePath string,
	irmaConfigurationPath string,
	androidStoragePath string,
	keyshareHandler KeyshareHandler,
) (*CredentialManager, error) {
	var err error
	if err = AssertPathExists(storagePath); err != nil {
		return nil, err
	}
	if err = AssertPathExists(irmaConfigurationPath); err != nil {
		return nil, err
	}

	var store *ConfigurationStore
	if store, err = NewConfigurationStore(storagePath+"/irma_configuration", irmaConfigurationPath); err != nil {
		return nil, err
	}
	if err = store.ParseFolder(); 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,
		ConfigurationStore:    store,
		storage:               storage{storagePath: storagePath, ConfigurationStore: store},
	}

	// Ensure storage path exists, and populate it with necessary files
Sietse Ringers's avatar
Sietse Ringers committed
116
	if err = cm.storage.EnsureStorageExists(); err != nil {
117
118
119
120
121
122
123
124
125
		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
126
	if cm.secretkey, err = cm.storage.LoadSecretKey(); err != nil {
127
128
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
129
	if cm.attributes, err = cm.storage.LoadAttributes(); err != nil {
130
131
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
132
	if cm.paillierKeyCache, err = cm.storage.LoadPaillierKeys(); err != nil {
133
134
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
135
	if cm.keyshareServers, err = cm.storage.LoadKeyshareServers(); err != nil {
136
137
138
139
140
141
142
143
144
145
		return nil, err
	}

	unenrolled := cm.unenrolledKeyshareServers()
	switch len(unenrolled) {
	case 0: // nop
	case 1:
		if keyshareHandler == nil {
			return nil, errors.New("Keyshare server found but no KeyshareHandler was given")
		}
Sietse Ringers's avatar
Sietse Ringers committed
146
147
		keyshareHandler.StartRegistration(unenrolled[0], func(email, pin string) error {
			return cm.KeyshareEnroll(unenrolled[0].Identifier(), email, pin)
148
149
150
151
152
153
154
155
		})
	default:
		return nil, errors.New("Too many keyshare servers")
	}

	return cm, nil
}

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

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

Sietse Ringers's avatar
Sietse Ringers committed
168
169
170
171
	sort.Sort(list)
	return list
}

Sietse Ringers's avatar
Sietse Ringers committed
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
// 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

207
208
209
210
211
212
213
214
215
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
216
217
218
		if err := cm.storage.StoreAttributes(cm.attributes); err != nil {
			return err
		}
219
220
221
222
223
224
225
226
227
228
229
	}

	// Remove credential
	if creds, exists := cm.credentials[id]; exists {
		if _, exists := creds[index]; exists {
			creds[index] = nil
			cm.credentials[id] = creds
		}
	}

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

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

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

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 {
260
	removed := map[CredentialTypeIdentifier][]TranslatedString{}
261
262
	list := cm.CredentialInfoList()
	for _, cred := range list {
263
264
265
		id := NewCredentialTypeIdentifier(cred.ID)
		removed[id] = cred.Attributes
		if err := cm.remove(id, cred.Index, false); err != nil {
266
267
268
			return err
		}
	}
Sietse Ringers's avatar
Sietse Ringers committed
269
	if err := cm.storage.StoreAttributes(cm.attributes); err != nil {
270
271
		return err
	}
272
273
274
275
276
277
278
279
280

	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
281
	return cm.storage.StoreLogs(cm.logs)
282
283
}

284
// Attribute and credential getter methods
Sietse Ringers's avatar
Sietse Ringers committed
285

Sietse Ringers's avatar
Sietse Ringers committed
286
// attrs returns cm.attributes[id], initializing it to an empty slice if neccesary
287
func (cm *CredentialManager) attrs(id CredentialTypeIdentifier) []*AttributeList {
Sietse Ringers's avatar
Sietse Ringers committed
288
289
290
291
292
293
294
295
296
	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
297
func (cm *CredentialManager) creds(id CredentialTypeIdentifier) map[int]*credential {
Sietse Ringers's avatar
Sietse Ringers committed
298
299
	list, exists := cm.credentials[id]
	if !exists {
Sietse Ringers's avatar
Sietse Ringers committed
300
		list = make(map[int]*credential)
Sietse Ringers's avatar
Sietse Ringers committed
301
302
303
304
305
		cm.credentials[id] = list
	}
	return list
}

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

315
316
317
318
319
320
321
322
323
324
325
326
327
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
328
	return cm.credential(id.Type, id.Index)
329
330
}

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

	return cm.credentials[id][counter], nil
368
369
}

Sietse Ringers's avatar
Sietse Ringers committed
370
// Methods used in the IRMA protocol
371

Sietse Ringers's avatar
Sietse Ringers committed
372
373
// Candidates returns a list of attributes present in this credential manager
// that satisfy the specified attribute disjunction.
374
func (cm *CredentialManager) Candidates(disjunction *AttributeDisjunction) []*AttributeIdentifier {
375
	candidates := make([]*AttributeIdentifier, 0, 10)
376
377

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

	return candidates
}

Sietse Ringers's avatar
Sietse Ringers committed
407
408
409
// CheckSatisfiability checks if this credential manager has the required attributes
// to satisfy the specifed disjunction list. If not, the unsatisfiable disjunctions
// are returned.
Sietse Ringers's avatar
Sietse Ringers committed
410
func (cm *CredentialManager) CheckSatisfiability(disjunctions AttributeDisjunctionList) AttributeDisjunctionList {
411
	missing := make(AttributeDisjunctionList, 0, 5)
Sietse Ringers's avatar
Sietse Ringers committed
412
	for _, disjunction := range disjunctions {
413
414
415
416
417
418
419
		if len(cm.Candidates(disjunction)) == 0 {
			missing = append(missing, disjunction)
		}
	}

	return missing
}
420

421
func (cm *CredentialManager) groupCredentials(choice *DisclosureChoice) (map[CredentialIdentifier][]int, error) {
422
423
424
425
426
427
428
429
430
	grouped := make(map[CredentialIdentifier][]int)

	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 {
431
432
			indices := make([]int, 1, 1)
			indices[0] = 1 // Always include metadata
433
434
435
436
437
438
			grouped[ici] = indices
		}

		if identifier.IsCredential() {
			continue // In this case we only disclose the metadata attribute, which is already handled
		}
439
		index, err := cm.ConfigurationStore.CredentialTypes[identifier.CredentialTypeIdentifier()].IndexOf(identifier)
440
441
442
443
		if err != nil {
			return nil, err
		}

Sietse Ringers's avatar
Sietse Ringers committed
444
		// These indices will be used in the []*big.Int at gabi.credential.Attributes,
445
		// which doesn't know about the secret key and metadata attribute, so +2
446
		grouped[ici] = append(grouped[ici], index+2)
447
448
449
450
451
	}

	return grouped, nil
}

452
// ProofBuilders constructs a list of proof builders for the specified attribute choice.
453
func (cm *CredentialManager) ProofBuilders(choice *DisclosureChoice) (gabi.ProofBuilderList, error) {
454
455
456
457
458
	todisclose, err := cm.groupCredentials(choice)
	if err != nil {
		return nil, err
	}

459
	builders := gabi.ProofBuilderList([]gabi.ProofBuilder{})
460
	for id, list := range todisclose {
Sietse Ringers's avatar
Sietse Ringers committed
461
		cred, err := cm.credentialByID(id)
462
463
464
465
466
		if err != nil {
			return nil, err
		}
		builders = append(builders, cred.Credential.CreateDisclosureProofBuilder(list))
	}
Sietse Ringers's avatar
Sietse Ringers committed
467
	return builders, nil
468
}
Sietse Ringers's avatar
Sietse Ringers committed
469

Sietse Ringers's avatar
Sietse Ringers committed
470
// Proofs computes disclosure proofs containing the attributes specified by choice.
471
func (cm *CredentialManager) Proofs(choice *DisclosureChoice, request IrmaSession, issig bool) (gabi.ProofList, error) {
Sietse Ringers's avatar
Sietse Ringers committed
472
	builders, err := cm.ProofBuilders(choice)
Sietse Ringers's avatar
Sietse Ringers committed
473
474
475
	if err != nil {
		return nil, err
	}
476
	return builders.BuildProofList(request.GetContext(), request.GetNonce(), issig), nil
Sietse Ringers's avatar
Sietse Ringers committed
477
478
}

479
480
// IssuanceProofBuilders constructs a list of proof builders in the issuance protocol
// for the future credentials as well as possibly any disclosed attributes.
481
func (cm *CredentialManager) IssuanceProofBuilders(request *IssuanceRequest) (gabi.ProofBuilderList, error) {
Sietse Ringers's avatar
Cleanup    
Sietse Ringers committed
482
	state, err := newIssuanceState()
Sietse Ringers's avatar
Sietse Ringers committed
483
484
485
486
487
	if err != nil {
		return nil, err
	}
	request.state = state

488
	proofBuilders := gabi.ProofBuilderList([]gabi.ProofBuilder{})
Sietse Ringers's avatar
Sietse Ringers committed
489
	for _, futurecred := range request.Credentials {
Sietse Ringers's avatar
Sietse Ringers committed
490
491
		var pk *gabi.PublicKey
		pk, err = cm.ConfigurationStore.PublicKey(futurecred.Credential.IssuerIdentifier(), futurecred.KeyCounter)
492
493
494
		if err != nil {
			return nil, err
		}
495
496
		credBuilder := gabi.NewCredentialBuilder(
			pk, request.GetContext(), cm.secretkey.Key, state.nonce2)
Sietse Ringers's avatar
Sietse Ringers committed
497
498
499
		request.state.builders = append(request.state.builders, credBuilder)
		proofBuilders = append(proofBuilders, credBuilder)
	}
Sietse Ringers's avatar
Sietse Ringers committed
500

Sietse Ringers's avatar
Sietse Ringers committed
501
	disclosures, err := cm.ProofBuilders(request.choice)
Sietse Ringers's avatar
Sietse Ringers committed
502
503
504
	if err != nil {
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
505
	proofBuilders = append(disclosures, proofBuilders...)
Sietse Ringers's avatar
Sietse Ringers committed
506
507
	return proofBuilders, nil
}
Sietse Ringers's avatar
Sietse Ringers committed
508

Sietse Ringers's avatar
Sietse Ringers committed
509
510
511
512
513
514
515
// 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
	}
516
	list := proofBuilders.BuildProofList(request.GetContext(), request.GetNonce(), false)
Sietse Ringers's avatar
Sietse Ringers committed
517
	return &gabi.IssueCommitmentMessage{Proofs: list, Nonce2: request.state.nonce2}, nil
Sietse Ringers's avatar
Sietse Ringers committed
518
519
}

Sietse Ringers's avatar
Sietse Ringers committed
520
521
// ConstructCredentials constructs and saves new credentials
// using the specified issuance signature messages.
Sietse Ringers's avatar
Sietse Ringers committed
522
523
524
525
526
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")
	}

527
528
	// 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
529
	gabicreds := []*gabi.Credential{}
Sietse Ringers's avatar
Sietse Ringers committed
530
	for i, sig := range msg {
531
		attrs, err := request.Credentials[i].AttributeList(cm.ConfigurationStore)
Sietse Ringers's avatar
Sietse Ringers committed
532
533
534
535
536
537
538
		if err != nil {
			return err
		}
		cred, err := request.state.builders[i].ConstructCredential(sig, attrs.Ints)
		if err != nil {
			return err
		}
539
		gabicreds = append(gabicreds, cred)
Sietse Ringers's avatar
Sietse Ringers committed
540
541
	}

542
	for _, gabicred := range gabicreds {
543
		newcred, err := newCredential(gabicred, cm.ConfigurationStore)
544
545
546
		if err != nil {
			return err
		}
Sietse Ringers's avatar
Sietse Ringers committed
547
548
549
		if err = cm.addCredential(newcred, true); err != nil {
			return err
		}
Sietse Ringers's avatar
Sietse Ringers committed
550
	}
551

Sietse Ringers's avatar
Sietse Ringers committed
552
	return nil
Sietse Ringers's avatar
Sietse Ringers committed
553
}
554

Sietse Ringers's avatar
Sietse Ringers committed
555
556
// Keyshare server handling

557
// PaillierKey returns a new Paillier key (and generates a new one in a goroutine).
Sietse Ringers's avatar
Sietse Ringers committed
558
func (cm *CredentialManager) paillierKey(wait bool) *paillierPrivateKey {
Sietse Ringers's avatar
Sietse Ringers committed
559
	cached := cm.paillierKeyCache
Sietse Ringers's avatar
Sietse Ringers committed
560
	ch := make(chan bool)
561
562
	go func() {
		newkey, _ := paillier.GenerateKey(rand.Reader, 2048)
Sietse Ringers's avatar
Sietse Ringers committed
563
564
		cm.paillierKeyCache = (*paillierPrivateKey)(newkey)
		if wait && cached == nil {
Sietse Ringers's avatar
Sietse Ringers committed
565
566
			ch <- true
		}
567
	}()
Sietse Ringers's avatar
Sietse Ringers committed
568
	if wait && cached == nil {
Sietse Ringers's avatar
Sietse Ringers committed
569
570
		<-ch
	}
Sietse Ringers's avatar
Sietse Ringers committed
571
	return cm.paillierKeyCache
572
}
Sietse Ringers's avatar
Sietse Ringers committed
573
574
575

func (cm *CredentialManager) unenrolledKeyshareServers() []*SchemeManager {
	list := []*SchemeManager{}
576
	for name, manager := range cm.ConfigurationStore.SchemeManagers {
Sietse Ringers's avatar
Sietse Ringers committed
577
578
579
580
581
582
		if _, contains := cm.keyshareServers[name]; len(manager.KeyshareServer) > 0 && !contains {
			list = append(list, manager)
		}
	}
	return list
}
Sietse Ringers's avatar
Sietse Ringers committed
583

584
585
// KeyshareEnroll attempts to register at the keyshare server of the specified scheme manager.
func (cm *CredentialManager) KeyshareEnroll(managerID SchemeManagerIdentifier, email, pin string) error {
586
	manager, ok := cm.ConfigurationStore.SchemeManagers[managerID]
Sietse Ringers's avatar
Sietse Ringers committed
587
588
589
590
591
592
593
594
595
596
597
	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)
598
	kss, err := newKeyshareServer(cm.paillierKey(true), manager.KeyshareServer, email)
Sietse Ringers's avatar
Sietse Ringers committed
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
	if err != nil {
		return err
	}
	message := keyshareRegistration{
		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
	}

614
	cm.keyshareServers[managerID] = kss
Sietse Ringers's avatar
Sietse Ringers committed
615
	return cm.storage.StoreKeyshareServers(cm.keyshareServers)
Sietse Ringers's avatar
Sietse Ringers committed
616
617
}

618
// KeyshareRemove unregisters the keyshare server of the specified scheme manager.
Sietse Ringers's avatar
Sietse Ringers committed
619
620
621
622
623
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
624
	return cm.storage.StoreKeyshareServers(cm.keyshareServers)
Sietse Ringers's avatar
Sietse Ringers committed
625
}
Sietse Ringers's avatar
Sietse Ringers committed
626

Sietse Ringers's avatar
Sietse Ringers committed
627
628
// Add, load and store log entries

629
func (cm *CredentialManager) addLogEntry(entry *LogEntry) error {
Sietse Ringers's avatar
Sietse Ringers committed
630
	cm.logs = append(cm.logs, entry)
631
	return cm.storage.StoreLogs(cm.logs)
632
	return nil
Sietse Ringers's avatar
Sietse Ringers committed
633
634
635
636
637
}

func (cm *CredentialManager) Logs() ([]*LogEntry, error) {
	if cm.logs == nil || len(cm.logs) == 0 {
		var err error
Sietse Ringers's avatar
Sietse Ringers committed
638
		cm.logs, err = cm.storage.LoadLogs()
Sietse Ringers's avatar
Sietse Ringers committed
639
640
641
642
643
644
		if err != nil {
			return nil, err
		}
	}
	return cm.logs, nil
}