manager.go 19.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
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
}

58
59
60
61
62
63
64
65
66
67
// 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
68
69
// NOTE: It is the responsibility of the caller that there exists a (properly
// protected) directory at storagePath!
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
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
103
	if err = cm.storage.EnsureStorageExists(); err != nil {
104
105
106
107
108
109
110
111
112
		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
113
	if cm.secretkey, err = cm.storage.LoadSecretKey(); err != nil {
114
115
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
116
	if cm.attributes, err = cm.storage.LoadAttributes(); err != nil {
117
118
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
119
	if cm.paillierKeyCache, err = cm.storage.LoadPaillierKeys(); err != nil {
120
121
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
122
	if cm.keyshareServers, err = cm.storage.LoadKeyshareServers(); err != nil {
123
124
125
126
127
128
129
130
131
132
		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
133
134
		keyshareHandler.StartRegistration(unenrolled[0], func(email, pin string) error {
			return cm.KeyshareEnroll(unenrolled[0].Identifier(), email, pin)
135
136
137
138
139
140
141
142
		})
	default:
		return nil, errors.New("Too many keyshare servers")
	}

	return cm, nil
}

143
144
145
// CredentialInfoList returns a list of information of all contained credentials.
func (cm *CredentialManager) CredentialInfoList() CredentialInfoList {
	list := CredentialInfoList([]*CredentialInfo{})
146
147

	for _, attrlistlist := range cm.attributes {
148
149
150
		for index, attrlist := range attrlistlist {
			info := attrlist.Info()
			info.Index = index
151
			list = append(list, info)
Sietse Ringers's avatar
Sietse Ringers committed
152
153
		}
	}
154

Sietse Ringers's avatar
Sietse Ringers committed
155
156
157
158
	sort.Sort(list)
	return list
}

Sietse Ringers's avatar
Sietse Ringers committed
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
186
187
188
189
190
191
192
193
// 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

194
195
196
197
198
199
200
201
202
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
203
204
205
		if err := cm.storage.StoreAttributes(cm.attributes); err != nil {
			return err
		}
206
207
208
209
210
211
212
213
214
215
216
	}

	// 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
217
	if err := cm.storage.DeleteSignature(attrs); err != nil {
218
219
220
		return err
	}

221
222
223
224
225
226
227
228
229
230
231
	removed := map[CredentialTypeIdentifier][]TranslatedString{}
	removed[id] = attrs.Strings()

	if storenow {
		return cm.addLogEntry(&LogEntry{
			Type:    actionRemoval,
			Time:    Timestamp(time.Now()),
			Removed: removed,
		})
	}
	return nil
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
}

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 {
247
	removed := map[CredentialTypeIdentifier][]TranslatedString{}
248
249
	list := cm.CredentialInfoList()
	for _, cred := range list {
250
251
252
		id := NewCredentialTypeIdentifier(cred.ID)
		removed[id] = cred.Attributes
		if err := cm.remove(id, cred.Index, false); err != nil {
253
254
255
			return err
		}
	}
Sietse Ringers's avatar
Sietse Ringers committed
256
	if err := cm.storage.StoreAttributes(cm.attributes); err != nil {
257
258
		return err
	}
259
260
261
262
263
264
265
266
267

	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
268
	return cm.storage.StoreLogs(cm.logs)
269
270
}

271
// Attribute and credential getter methods
Sietse Ringers's avatar
Sietse Ringers committed
272

Sietse Ringers's avatar
Sietse Ringers committed
273
// attrs returns cm.attributes[id], initializing it to an empty slice if neccesary
274
func (cm *CredentialManager) attrs(id CredentialTypeIdentifier) []*AttributeList {
Sietse Ringers's avatar
Sietse Ringers committed
275
276
277
278
279
280
281
282
283
	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
284
func (cm *CredentialManager) creds(id CredentialTypeIdentifier) map[int]*credential {
Sietse Ringers's avatar
Sietse Ringers committed
285
286
	list, exists := cm.credentials[id]
	if !exists {
Sietse Ringers's avatar
Sietse Ringers committed
287
		list = make(map[int]*credential)
Sietse Ringers's avatar
Sietse Ringers committed
288
289
290
291
292
		cm.credentials[id] = list
	}
	return list
}

Sietse Ringers's avatar
Sietse Ringers committed
293
// Attributes returns the attribute list of the requested credential, or nil if we do not have it.
294
func (cm *CredentialManager) Attributes(id CredentialTypeIdentifier, counter int) (attributes *AttributeList) {
Sietse Ringers's avatar
Sietse Ringers committed
295
296
	list := cm.attrs(id)
	if len(list) <= counter {
Sietse Ringers's avatar
Sietse Ringers committed
297
298
299
300
301
		return
	}
	return list[counter]
}

302
303
304
305
306
307
308
309
310
311
312
313
314
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
315
	return cm.credential(id.Type, id.Index)
316
317
}

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

	return cm.credentials[id][counter], nil
355
356
}

Sietse Ringers's avatar
Sietse Ringers committed
357
// Methods used in the IRMA protocol
358

Sietse Ringers's avatar
Sietse Ringers committed
359
360
// Candidates returns a list of attributes present in this credential manager
// that satisfy the specified attribute disjunction.
361
func (cm *CredentialManager) Candidates(disjunction *AttributeDisjunction) []*AttributeIdentifier {
362
	candidates := make([]*AttributeIdentifier, 0, 10)
363
364

	for _, attribute := range disjunction.Attributes {
Sietse Ringers's avatar
Sietse Ringers committed
365
		credID := attribute.CredentialTypeIdentifier()
366
		if !cm.ConfigurationStore.Contains(credID) {
367
368
			continue
		}
Sietse Ringers's avatar
Sietse Ringers committed
369
		creds := cm.credentials[credID]
370
371
372
373
374
375
376
377
378
		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 {
379
				attrs := cred.AttributeList()
Sietse Ringers's avatar
Sietse Ringers committed
380
				val := attrs.untranslatedAttribute(attribute)
381
				if val == "" { // This won't handle empty attributes correctly
382
383
384
385
386
387
388
389
390
391
392
393
					continue
				}
				if !disjunction.HasValues() || val == disjunction.Values[attribute] {
					candidates = append(candidates, id)
				}
			}
		}
	}

	return candidates
}

Sietse Ringers's avatar
Sietse Ringers committed
394
395
396
// 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
397
func (cm *CredentialManager) CheckSatisfiability(disjunctions AttributeDisjunctionList) AttributeDisjunctionList {
398
	missing := make(AttributeDisjunctionList, 0, 5)
Sietse Ringers's avatar
Sietse Ringers committed
399
	for _, disjunction := range disjunctions {
400
401
402
403
404
405
406
		if len(cm.Candidates(disjunction)) == 0 {
			missing = append(missing, disjunction)
		}
	}

	return missing
}
407

408
func (cm *CredentialManager) groupCredentials(choice *DisclosureChoice) (map[CredentialIdentifier][]int, error) {
409
410
411
412
413
414
415
416
417
	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 {
418
419
			indices := make([]int, 1, 1)
			indices[0] = 1 // Always include metadata
420
421
422
423
424
425
			grouped[ici] = indices
		}

		if identifier.IsCredential() {
			continue // In this case we only disclose the metadata attribute, which is already handled
		}
426
		index, err := cm.ConfigurationStore.Credentials[identifier.CredentialTypeIdentifier()].IndexOf(identifier)
427
428
429
430
		if err != nil {
			return nil, err
		}

Sietse Ringers's avatar
Sietse Ringers committed
431
		// These indices will be used in the []*big.Int at gabi.credential.Attributes,
432
		// which doesn't know about the secret key and metadata attribute, so +2
433
		grouped[ici] = append(grouped[ici], index+2)
434
435
436
437
438
	}

	return grouped, nil
}

439
// ProofBuilders constructs a list of proof builders for the specified attribute choice.
440
func (cm *CredentialManager) ProofBuilders(choice *DisclosureChoice) (gabi.ProofBuilderList, error) {
441
442
443
444
445
	todisclose, err := cm.groupCredentials(choice)
	if err != nil {
		return nil, err
	}

446
	builders := gabi.ProofBuilderList([]gabi.ProofBuilder{})
447
	for id, list := range todisclose {
Sietse Ringers's avatar
Sietse Ringers committed
448
		cred, err := cm.credentialByID(id)
449
450
451
452
453
		if err != nil {
			return nil, err
		}
		builders = append(builders, cred.Credential.CreateDisclosureProofBuilder(list))
	}
Sietse Ringers's avatar
Sietse Ringers committed
454
	return builders, nil
455
}
Sietse Ringers's avatar
Sietse Ringers committed
456

Sietse Ringers's avatar
Sietse Ringers committed
457
// Proofs computes disclosure proofs containing the attributes specified by choice.
458
func (cm *CredentialManager) Proofs(choice *DisclosureChoice, request IrmaSession, issig bool) (gabi.ProofList, error) {
Sietse Ringers's avatar
Sietse Ringers committed
459
	builders, err := cm.ProofBuilders(choice)
Sietse Ringers's avatar
Sietse Ringers committed
460
461
462
	if err != nil {
		return nil, err
	}
463
	return builders.BuildProofList(request.GetContext(), request.GetNonce(), issig), nil
Sietse Ringers's avatar
Sietse Ringers committed
464
465
}

466
467
// IssuanceProofBuilders constructs a list of proof builders in the issuance protocol
// for the future credentials as well as possibly any disclosed attributes.
468
func (cm *CredentialManager) IssuanceProofBuilders(request *IssuanceRequest) (gabi.ProofBuilderList, error) {
Sietse Ringers's avatar
Cleanup    
Sietse Ringers committed
469
	state, err := newIssuanceState()
Sietse Ringers's avatar
Sietse Ringers committed
470
471
472
473
474
	if err != nil {
		return nil, err
	}
	request.state = state

475
	proofBuilders := gabi.ProofBuilderList([]gabi.ProofBuilder{})
Sietse Ringers's avatar
Sietse Ringers committed
476
	for _, futurecred := range request.Credentials {
Sietse Ringers's avatar
Sietse Ringers committed
477
478
		var pk *gabi.PublicKey
		pk, err = cm.ConfigurationStore.PublicKey(futurecred.Credential.IssuerIdentifier(), futurecred.KeyCounter)
479
480
481
		if err != nil {
			return nil, err
		}
482
483
		credBuilder := gabi.NewCredentialBuilder(
			pk, request.GetContext(), cm.secretkey.Key, state.nonce2)
Sietse Ringers's avatar
Sietse Ringers committed
484
485
486
		request.state.builders = append(request.state.builders, credBuilder)
		proofBuilders = append(proofBuilders, credBuilder)
	}
Sietse Ringers's avatar
Sietse Ringers committed
487

Sietse Ringers's avatar
Sietse Ringers committed
488
	disclosures, err := cm.ProofBuilders(request.choice)
Sietse Ringers's avatar
Sietse Ringers committed
489
490
491
	if err != nil {
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
492
	proofBuilders = append(disclosures, proofBuilders...)
Sietse Ringers's avatar
Sietse Ringers committed
493
494
	return proofBuilders, nil
}
Sietse Ringers's avatar
Sietse Ringers committed
495

Sietse Ringers's avatar
Sietse Ringers committed
496
497
498
499
500
501
502
// 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
	}
503
	list := proofBuilders.BuildProofList(request.GetContext(), request.GetNonce(), false)
Sietse Ringers's avatar
Sietse Ringers committed
504
	return &gabi.IssueCommitmentMessage{Proofs: list, Nonce2: request.state.nonce2}, nil
Sietse Ringers's avatar
Sietse Ringers committed
505
506
}

Sietse Ringers's avatar
Sietse Ringers committed
507
508
// ConstructCredentials constructs and saves new credentials
// using the specified issuance signature messages.
Sietse Ringers's avatar
Sietse Ringers committed
509
510
511
512
513
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")
	}

514
515
	// 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
516
	gabicreds := []*gabi.Credential{}
Sietse Ringers's avatar
Sietse Ringers committed
517
	for i, sig := range msg {
518
		attrs, err := request.Credentials[i].AttributeList(cm.ConfigurationStore)
Sietse Ringers's avatar
Sietse Ringers committed
519
520
521
522
523
524
525
		if err != nil {
			return err
		}
		cred, err := request.state.builders[i].ConstructCredential(sig, attrs.Ints)
		if err != nil {
			return err
		}
526
		gabicreds = append(gabicreds, cred)
Sietse Ringers's avatar
Sietse Ringers committed
527
528
	}

529
	for _, gabicred := range gabicreds {
530
		newcred, err := newCredential(gabicred, cm.ConfigurationStore)
531
532
533
		if err != nil {
			return err
		}
Sietse Ringers's avatar
Sietse Ringers committed
534
535
536
		if err = cm.addCredential(newcred, true); err != nil {
			return err
		}
Sietse Ringers's avatar
Sietse Ringers committed
537
	}
538

Sietse Ringers's avatar
Sietse Ringers committed
539
	return nil
Sietse Ringers's avatar
Sietse Ringers committed
540
}
541

Sietse Ringers's avatar
Sietse Ringers committed
542
543
// Keyshare server handling

544
// PaillierKey returns a new Paillier key (and generates a new one in a goroutine).
Sietse Ringers's avatar
Sietse Ringers committed
545
func (cm *CredentialManager) paillierKey(wait bool) *paillierPrivateKey {
Sietse Ringers's avatar
Sietse Ringers committed
546
	cached := cm.paillierKeyCache
Sietse Ringers's avatar
Sietse Ringers committed
547
	ch := make(chan bool)
548
549
	go func() {
		newkey, _ := paillier.GenerateKey(rand.Reader, 2048)
Sietse Ringers's avatar
Sietse Ringers committed
550
551
		cm.paillierKeyCache = (*paillierPrivateKey)(newkey)
		if wait && cached == nil {
Sietse Ringers's avatar
Sietse Ringers committed
552
553
			ch <- true
		}
554
	}()
Sietse Ringers's avatar
Sietse Ringers committed
555
	if wait && cached == nil {
Sietse Ringers's avatar
Sietse Ringers committed
556
557
		<-ch
	}
Sietse Ringers's avatar
Sietse Ringers committed
558
	return cm.paillierKeyCache
559
}
Sietse Ringers's avatar
Sietse Ringers committed
560
561
562

func (cm *CredentialManager) unenrolledKeyshareServers() []*SchemeManager {
	list := []*SchemeManager{}
563
	for name, manager := range cm.ConfigurationStore.SchemeManagers {
Sietse Ringers's avatar
Sietse Ringers committed
564
565
566
567
568
569
		if _, contains := cm.keyshareServers[name]; len(manager.KeyshareServer) > 0 && !contains {
			list = append(list, manager)
		}
	}
	return list
}
Sietse Ringers's avatar
Sietse Ringers committed
570

571
572
// KeyshareEnroll attempts to register at the keyshare server of the specified scheme manager.
func (cm *CredentialManager) KeyshareEnroll(managerID SchemeManagerIdentifier, email, pin string) error {
573
	manager, ok := cm.ConfigurationStore.SchemeManagers[managerID]
Sietse Ringers's avatar
Sietse Ringers committed
574
575
576
577
578
579
580
581
582
583
584
	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)
585
	kss, err := newKeyshareServer(cm.paillierKey(true), manager.KeyshareServer, email)
Sietse Ringers's avatar
Sietse Ringers committed
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
	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
	}

601
	cm.keyshareServers[managerID] = kss
Sietse Ringers's avatar
Sietse Ringers committed
602
	return cm.storage.StoreKeyshareServers(cm.keyshareServers)
Sietse Ringers's avatar
Sietse Ringers committed
603
604
}

605
// KeyshareRemove unregisters the keyshare server of the specified scheme manager.
Sietse Ringers's avatar
Sietse Ringers committed
606
607
608
609
610
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
611
	return cm.storage.StoreKeyshareServers(cm.keyshareServers)
Sietse Ringers's avatar
Sietse Ringers committed
612
}
Sietse Ringers's avatar
Sietse Ringers committed
613

Sietse Ringers's avatar
Sietse Ringers committed
614
615
// Add, load and store log entries

616
func (cm *CredentialManager) addLogEntry(entry *LogEntry) error {
Sietse Ringers's avatar
Sietse Ringers committed
617
	cm.logs = append(cm.logs, entry)
618
	return cm.storage.StoreLogs(cm.logs)
619
	return nil
Sietse Ringers's avatar
Sietse Ringers committed
620
621
622
623
624
}

func (cm *CredentialManager) Logs() ([]*LogEntry, error) {
	if cm.logs == nil || len(cm.logs) == 0 {
		var err error
Sietse Ringers's avatar
Sietse Ringers committed
625
		cm.logs, err = cm.storage.LoadLogs()
Sietse Ringers's avatar
Sietse Ringers committed
626
627
628
629
630
631
		if err != nil {
			return nil, err
		}
	}
	return cm.logs, nil
}