manager.go 18.1 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
14
15
	"github.com/mhe/gabi"
)

// CredentialManager manages credentials.
type CredentialManager struct {
16
	secretkey        *secretKey
17
18
19
	attributes       map[CredentialTypeIdentifier][]*AttributeList
	credentials      map[CredentialTypeIdentifier]map[int]*credential
	keyshareServers  map[SchemeManagerIdentifier]*keyshareServer
20
	paillierKeyCache *paillierPrivateKey
Sietse Ringers's avatar
Sietse Ringers committed
21
	logs             []*LogEntry
22

23
	storage               storage
24
	irmaConfigurationPath string
25
	androidStoragePath    string
26
	ConfigurationStore    *ConfigurationStore
27
	updates               []update
Sietse Ringers's avatar
Sietse Ringers committed
28
29
}

30
31
32
33
type secretKey struct {
	Key *big.Int
}

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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),
// 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.
//
// NOTE: It is the responsibility of the caller that there exists a directory
// at storagePath!
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
79
	if err = cm.storage.EnsureStorageExists(); err != nil {
80
81
82
83
84
85
86
87
88
		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
89
	if cm.secretkey, err = cm.storage.LoadSecretKey(); err != nil {
90
91
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
92
	if cm.attributes, err = cm.storage.LoadAttributes(); err != nil {
93
94
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
95
	if cm.paillierKeyCache, err = cm.storage.LoadPaillierKeys(); err != nil {
96
97
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
98
	if cm.keyshareServers, err = cm.storage.LoadKeyshareServers(); err != nil {
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
		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")
		}
		keyshareHandler.StartRegistration(unenrolled[0], func(email, pin string) {
			cm.KeyshareEnroll(unenrolled[0].Identifier(), email, pin)
		})
	default:
		return nil, errors.New("Too many keyshare servers")
	}

	return cm, nil
}

119
120
121
// CredentialInfoList returns a list of information of all contained credentials.
func (cm *CredentialManager) CredentialInfoList() CredentialInfoList {
	list := CredentialInfoList([]*CredentialInfo{})
122
123

	for _, attrlistlist := range cm.attributes {
124
125
126
		for index, attrlist := range attrlistlist {
			info := attrlist.Info()
			info.Index = index
127
			list = append(list, attrlist.Info())
Sietse Ringers's avatar
Sietse Ringers committed
128
129
		}
	}
130

Sietse Ringers's avatar
Sietse Ringers committed
131
132
133
134
	sort.Sort(list)
	return list
}

135
136
137
138
139
140
141
142
143
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
144
		cm.storage.StoreAttributes(cm.attributes)
145
146
147
148
149
150
151
152
153
154
155
	}

	// 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
156
	if err := cm.storage.DeleteSignature(attrs); err != nil {
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
		return err
	}

	return cm.addLogEntry(&LogEntry{
		Type:              actionRemoval,
		Time:              Timestamp(time.Now()),
		RemovedCredential: id,
	}, storenow)
}

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 {
	list := cm.CredentialInfoList()
	for _, cred := range list {
		if err := cm.remove(NewCredentialTypeIdentifier(cred.ID), cred.Index, false); err != nil {
			return err
		}
	}
Sietse Ringers's avatar
Sietse Ringers committed
186
	if err := cm.storage.StoreAttributes(cm.attributes); err != nil {
187
188
		return err
	}
Sietse Ringers's avatar
Sietse Ringers committed
189
	return cm.storage.StoreLogs(cm.logs)
190
191
}

Sietse Ringers's avatar
Sietse Ringers committed
192
// attrs returns cm.attributes[id], initializing it to an empty slice if neccesary
193
func (cm *CredentialManager) attrs(id CredentialTypeIdentifier) []*AttributeList {
Sietse Ringers's avatar
Sietse Ringers committed
194
195
196
197
198
199
200
201
202
	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
203
func (cm *CredentialManager) creds(id CredentialTypeIdentifier) map[int]*credential {
Sietse Ringers's avatar
Sietse Ringers committed
204
205
	list, exists := cm.credentials[id]
	if !exists {
Sietse Ringers's avatar
Sietse Ringers committed
206
		list = make(map[int]*credential)
Sietse Ringers's avatar
Sietse Ringers committed
207
208
209
210
211
		cm.credentials[id] = list
	}
	return list
}

Sietse Ringers's avatar
Sietse Ringers committed
212
// Attributes returns the attribute list of the requested credential, or nil if we do not have it.
213
func (cm *CredentialManager) Attributes(id CredentialTypeIdentifier, counter int) (attributes *AttributeList) {
Sietse Ringers's avatar
Sietse Ringers committed
214
215
	list := cm.attrs(id)
	if len(list) <= counter {
Sietse Ringers's avatar
Sietse Ringers committed
216
217
218
219
220
		return
	}
	return list[counter]
}

221
222
223
224
225
226
227
228
229
230
231
232
233
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
234
	return cm.credential(id.Type, id.Index)
235
236
}

Sietse Ringers's avatar
Sietse Ringers committed
237
238
// 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
239
	// If the requested credential is not in credential map, we check if its attributes were
240
	// deserialized during NewCredentialManager(). If so, there should be a corresponding signature file,
Sietse Ringers's avatar
Sietse Ringers committed
241
	// so we read that, construct the credential, and add it to the credential map
Sietse Ringers's avatar
Sietse Ringers committed
242
	if _, exists := cm.creds(id)[counter]; !exists {
Sietse Ringers's avatar
Sietse Ringers committed
243
244
245
246
		attrs := cm.Attributes(id, counter)
		if attrs == nil { // We do not have the requested cred
			return
		}
Sietse Ringers's avatar
Sietse Ringers committed
247
		sig, err := cm.storage.LoadSignature(attrs)
Sietse Ringers's avatar
Sietse Ringers committed
248
249
250
251
252
253
254
		if err != nil {
			return nil, err
		}
		if sig == nil {
			err = errors.New("signature file not found")
			return nil, err
		}
255
		pk, err := attrs.PublicKey()
256
257
258
		if err != nil {
			return nil, err
		}
259
260
261
		if pk == nil {
			return nil, errors.New("unknown public key")
		}
262
		cred, err := newCredential(&gabi.Credential{
263
			Attributes: append([]*big.Int{cm.secretkey.Key}, attrs.Ints...),
264
			Signature:  sig,
265
			Pk:         pk,
266
		}, cm.ConfigurationStore)
267
268
269
		if err != nil {
			return nil, err
		}
Sietse Ringers's avatar
Sietse Ringers committed
270
271
272
273
		cm.credentials[id][counter] = cred
	}

	return cm.credentials[id][counter], nil
274
275
}

276
277
278
// 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) {
279
	id := cred.CredentialType().Identifier()
280
	cm.attributes[id] = append(cm.attrs(id), cred.AttributeList())
281

282
	if _, exists := cm.credentials[id]; !exists {
Sietse Ringers's avatar
Sietse Ringers committed
283
		cm.credentials[id] = make(map[int]*credential)
284
	}
285
286
287
288
289
290
	if cred.CredentialType().IsSingleton {
		cm.credentials[id][0] = cred
	} else {
		counter := len(cm.attributes[id]) - 1
		cm.credentials[id][counter] = cred
	}
291

Sietse Ringers's avatar
Sietse Ringers committed
292
	if err = cm.storage.StoreSignature(cred); err != nil {
293
294
		return
	}
295
	if storeAttributes {
Sietse Ringers's avatar
Sietse Ringers committed
296
		err = cm.storage.StoreAttributes(cm.attributes)
297
	}
298
299
	return
}
300

Sietse Ringers's avatar
Sietse Ringers committed
301
302
// Candidates returns a list of attributes present in this credential manager
// that satisfy the specified attribute disjunction.
303
func (cm *CredentialManager) Candidates(disjunction *AttributeDisjunction) []*AttributeIdentifier {
304
	candidates := make([]*AttributeIdentifier, 0, 10)
305
306

	for _, attribute := range disjunction.Attributes {
Sietse Ringers's avatar
Sietse Ringers committed
307
		credID := attribute.CredentialTypeIdentifier()
308
		if !cm.ConfigurationStore.Contains(credID) {
309
310
			continue
		}
Sietse Ringers's avatar
Sietse Ringers committed
311
		creds := cm.credentials[credID]
312
313
314
315
316
317
318
319
320
		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 {
321
				attrs := cred.AttributeList()
Sietse Ringers's avatar
Sietse Ringers committed
322
				val := attrs.untranslatedAttribute(attribute)
323
				if val == "" { // This won't handle empty attributes correctly
324
325
326
327
328
329
330
331
332
333
334
335
					continue
				}
				if !disjunction.HasValues() || val == disjunction.Values[attribute] {
					candidates = append(candidates, id)
				}
			}
		}
	}

	return candidates
}

Sietse Ringers's avatar
Sietse Ringers committed
336
337
338
// 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
339
func (cm *CredentialManager) CheckSatisfiability(disjunctions AttributeDisjunctionList) AttributeDisjunctionList {
340
	missing := make(AttributeDisjunctionList, 0, 5)
Sietse Ringers's avatar
Sietse Ringers committed
341
	for _, disjunction := range disjunctions {
342
343
344
345
346
347
348
		if len(cm.Candidates(disjunction)) == 0 {
			missing = append(missing, disjunction)
		}
	}

	return missing
}
349

350
func (cm *CredentialManager) groupCredentials(choice *DisclosureChoice) (map[CredentialIdentifier][]int, error) {
351
352
353
354
355
356
357
358
359
	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 {
360
361
			indices := make([]int, 1, 1)
			indices[0] = 1 // Always include metadata
362
363
364
365
366
367
			grouped[ici] = indices
		}

		if identifier.IsCredential() {
			continue // In this case we only disclose the metadata attribute, which is already handled
		}
368
		index, err := cm.ConfigurationStore.Credentials[identifier.CredentialTypeIdentifier()].IndexOf(identifier)
369
370
371
372
		if err != nil {
			return nil, err
		}

Sietse Ringers's avatar
Sietse Ringers committed
373
		// These indices will be used in the []*big.Int at gabi.credential.Attributes,
374
		// which doesn't know about the secret key and metadata attribute, so +2
375
		grouped[ici] = append(grouped[ici], index+2)
376
377
378
379
380
	}

	return grouped, nil
}

381
382
// IrmaSession is an IRMA session.
type IrmaSession interface {
383
	GetNonce() *big.Int
Sietse Ringers's avatar
Sietse Ringers committed
384
	SetNonce(*big.Int)
385
	GetContext() *big.Int
Sietse Ringers's avatar
Sietse Ringers committed
386
387
	SetContext(*big.Int)
	DisjunctionList() AttributeDisjunctionList
Sietse Ringers's avatar
Sietse Ringers committed
388
389
	DisclosureChoice() *DisclosureChoice
	SetDisclosureChoice(choice *DisclosureChoice)
390
	Distributed(store *ConfigurationStore) bool
Sietse Ringers's avatar
Sietse Ringers committed
391
	SchemeManagers() []SchemeManagerIdentifier
392
393
}

394
// ProofBuilders constructs a list of proof builders for the specified attribute choice.
395
func (cm *CredentialManager) ProofBuilders(choice *DisclosureChoice) (gabi.ProofBuilderList, error) {
396
397
398
399
400
	todisclose, err := cm.groupCredentials(choice)
	if err != nil {
		return nil, err
	}

401
	builders := gabi.ProofBuilderList([]gabi.ProofBuilder{})
402
	for id, list := range todisclose {
Sietse Ringers's avatar
Sietse Ringers committed
403
		cred, err := cm.credentialByID(id)
404
405
406
407
408
		if err != nil {
			return nil, err
		}
		builders = append(builders, cred.Credential.CreateDisclosureProofBuilder(list))
	}
Sietse Ringers's avatar
Sietse Ringers committed
409
	return builders, nil
410
}
Sietse Ringers's avatar
Sietse Ringers committed
411

Sietse Ringers's avatar
Sietse Ringers committed
412
// Proofs computes disclosure proofs containing the attributes specified by choice.
413
func (cm *CredentialManager) Proofs(choice *DisclosureChoice, request IrmaSession, issig bool) (gabi.ProofList, error) {
Sietse Ringers's avatar
Sietse Ringers committed
414
	builders, err := cm.ProofBuilders(choice)
Sietse Ringers's avatar
Sietse Ringers committed
415
416
417
	if err != nil {
		return nil, err
	}
418
	return builders.BuildProofList(request.GetContext(), request.GetNonce(), issig), nil
Sietse Ringers's avatar
Sietse Ringers committed
419
420
}

421
422
// IssuanceProofBuilders constructs a list of proof builders in the issuance protocol
// for the future credentials as well as possibly any disclosed attributes.
423
func (cm *CredentialManager) IssuanceProofBuilders(request *IssuanceRequest) (gabi.ProofBuilderList, error) {
Sietse Ringers's avatar
Cleanup    
Sietse Ringers committed
424
	state, err := newIssuanceState()
Sietse Ringers's avatar
Sietse Ringers committed
425
426
427
428
429
	if err != nil {
		return nil, err
	}
	request.state = state

430
	proofBuilders := gabi.ProofBuilderList([]gabi.ProofBuilder{})
Sietse Ringers's avatar
Sietse Ringers committed
431
	for _, futurecred := range request.Credentials {
Sietse Ringers's avatar
Sietse Ringers committed
432
433
		var pk *gabi.PublicKey
		pk, err = cm.ConfigurationStore.PublicKey(futurecred.Credential.IssuerIdentifier(), futurecred.KeyCounter)
434
435
436
		if err != nil {
			return nil, err
		}
437
438
		credBuilder := gabi.NewCredentialBuilder(
			pk, request.GetContext(), cm.secretkey.Key, state.nonce2)
Sietse Ringers's avatar
Sietse Ringers committed
439
440
441
		request.state.builders = append(request.state.builders, credBuilder)
		proofBuilders = append(proofBuilders, credBuilder)
	}
Sietse Ringers's avatar
Sietse Ringers committed
442

Sietse Ringers's avatar
Sietse Ringers committed
443
	disclosures, err := cm.ProofBuilders(request.choice)
Sietse Ringers's avatar
Sietse Ringers committed
444
445
446
	if err != nil {
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
447
	proofBuilders = append(disclosures, proofBuilders...)
Sietse Ringers's avatar
Sietse Ringers committed
448
449
	return proofBuilders, nil
}
Sietse Ringers's avatar
Sietse Ringers committed
450

Sietse Ringers's avatar
Sietse Ringers committed
451
452
453
454
455
456
457
// 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
	}
458
	list := proofBuilders.BuildProofList(request.GetContext(), request.GetNonce(), false)
Sietse Ringers's avatar
Sietse Ringers committed
459
	return &gabi.IssueCommitmentMessage{Proofs: list, Nonce2: request.state.nonce2}, nil
Sietse Ringers's avatar
Sietse Ringers committed
460
461
}

Sietse Ringers's avatar
Sietse Ringers committed
462
463
// ConstructCredentials constructs and saves new credentials
// using the specified issuance signature messages.
Sietse Ringers's avatar
Sietse Ringers committed
464
465
466
467
468
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")
	}

469
470
	// 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
471
	gabicreds := []*gabi.Credential{}
Sietse Ringers's avatar
Sietse Ringers committed
472
	for i, sig := range msg {
473
		attrs, err := request.Credentials[i].AttributeList(cm.ConfigurationStore)
Sietse Ringers's avatar
Sietse Ringers committed
474
475
476
477
478
479
480
		if err != nil {
			return err
		}
		cred, err := request.state.builders[i].ConstructCredential(sig, attrs.Ints)
		if err != nil {
			return err
		}
481
		gabicreds = append(gabicreds, cred)
Sietse Ringers's avatar
Sietse Ringers committed
482
483
	}

484
	for _, gabicred := range gabicreds {
485
		newcred, err := newCredential(gabicred, cm.ConfigurationStore)
486
487
488
489
		if err != nil {
			return err
		}
		cm.addCredential(newcred, true)
Sietse Ringers's avatar
Sietse Ringers committed
490
	}
491

Sietse Ringers's avatar
Sietse Ringers committed
492
	return nil
Sietse Ringers's avatar
Sietse Ringers committed
493
}
494
495

// PaillierKey returns a new Paillier key (and generates a new one in a goroutine).
Sietse Ringers's avatar
Sietse Ringers committed
496
func (cm *CredentialManager) paillierKey(wait bool) *paillierPrivateKey {
497
	retval := cm.paillierKeyCache
Sietse Ringers's avatar
Sietse Ringers committed
498
	ch := make(chan bool)
499
500
501
502
	go func() {
		newkey, _ := paillier.GenerateKey(rand.Reader, 2048)
		converted := paillierPrivateKey(*newkey)
		cm.paillierKeyCache = &converted
Sietse Ringers's avatar
Sietse Ringers committed
503
504
505
		if wait && retval == nil {
			ch <- true
		}
506
	}()
Sietse Ringers's avatar
Sietse Ringers committed
507
508
509
510
	if wait && retval == nil {
		<-ch
		return cm.paillierKeyCache
	}
511
512
	return retval
}
Sietse Ringers's avatar
Sietse Ringers committed
513
514
515

func (cm *CredentialManager) unenrolledKeyshareServers() []*SchemeManager {
	list := []*SchemeManager{}
516
	for name, manager := range cm.ConfigurationStore.SchemeManagers {
Sietse Ringers's avatar
Sietse Ringers committed
517
518
519
520
521
522
		if _, contains := cm.keyshareServers[name]; len(manager.KeyshareServer) > 0 && !contains {
			list = append(list, manager)
		}
	}
	return list
}
Sietse Ringers's avatar
Sietse Ringers committed
523

524
525
// KeyshareEnroll attempts to register at the keyshare server of the specified scheme manager.
func (cm *CredentialManager) KeyshareEnroll(managerID SchemeManagerIdentifier, email, pin string) error {
526
	manager, ok := cm.ConfigurationStore.SchemeManagers[managerID]
Sietse Ringers's avatar
Sietse Ringers committed
527
528
529
530
531
532
533
534
535
536
537
	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)
538
	kss, err := newKeyshareServer(cm.paillierKey(true), manager.KeyshareServer, email)
Sietse Ringers's avatar
Sietse Ringers committed
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
	if err != nil {
		return err
	}
	message := keyshareRegistration{
		Username:  email,
		Pin:       kss.HashedPin(pin),
		PublicKey: (*paillierPublicKey)(&kss.PrivateKey.PublicKey),
	}

	// TODO: examine error returned by Post() to see if it tells us that the email address is already in use
	result := &struct{}{}
	err = transport.Post("web/users/selfenroll", result, message)
	if err != nil {
		return err
	}

555
	cm.keyshareServers[managerID] = kss
Sietse Ringers's avatar
Sietse Ringers committed
556
	return cm.storage.StoreKeyshareServers(cm.keyshareServers)
Sietse Ringers's avatar
Sietse Ringers committed
557
558
}

559
// KeyshareRemove unregisters the keyshare server of the specified scheme manager.
Sietse Ringers's avatar
Sietse Ringers committed
560
561
562
563
564
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
565
	return cm.storage.StoreKeyshareServers(cm.keyshareServers)
Sietse Ringers's avatar
Sietse Ringers committed
566
}
Sietse Ringers's avatar
Sietse Ringers committed
567

568
func (cm *CredentialManager) addLogEntry(entry *LogEntry, storenow bool) error {
Sietse Ringers's avatar
Sietse Ringers committed
569
	cm.logs = append(cm.logs, entry)
570
	if storenow {
Sietse Ringers's avatar
Sietse Ringers committed
571
		return cm.storage.StoreLogs(cm.logs)
572
573
	}
	return nil
Sietse Ringers's avatar
Sietse Ringers committed
574
575
576
577
578
}

func (cm *CredentialManager) Logs() ([]*LogEntry, error) {
	if cm.logs == nil || len(cm.logs) == 0 {
		var err error
Sietse Ringers's avatar
Sietse Ringers committed
579
		cm.logs, err = cm.storage.LoadLogs()
Sietse Ringers's avatar
Sietse Ringers committed
580
581
582
583
584
585
		if err != nil {
			return nil, err
		}
	}
	return cm.logs, nil
}
Sietse Ringers's avatar
Sietse Ringers committed
586
587
588
589
590
591
592
593

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