manager.go 19.7 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
52
	keyshareHandler       KeyshareHandler
Sietse Ringers's avatar
Sietse Ringers committed
53
54
}

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

59
60
61
62
63
64
65
66
67
68
// 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
69
70
// NOTE: It is the responsibility of the caller that there exists a (properly
// protected) directory at storagePath!
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
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
	}

	cm := &CredentialManager{
		credentials:           make(map[CredentialTypeIdentifier]map[int]*credential),
		keyshareServers:       make(map[SchemeManagerIdentifier]*keyshareServer),
		attributes:            make(map[CredentialTypeIdentifier][]*AttributeList),
		irmaConfigurationPath: irmaConfigurationPath,
		androidStoragePath:    androidStoragePath,
91
92
93
94
95
96
97
98
99
		keyshareHandler:       keyshareHandler,
	}

	cm.ConfigurationStore, err = NewConfigurationStore(storagePath+"/irma_configuration", irmaConfigurationPath)
	if err != nil {
		return nil, err
	}
	if err = cm.ConfigurationStore.ParseFolder(); err != nil {
		return nil, err
100
101
102
	}

	// Ensure storage path exists, and populate it with necessary files
103
	cm.storage = storage{storagePath: storagePath, ConfigurationStore: cm.ConfigurationStore}
Sietse Ringers's avatar
Sietse Ringers committed
104
	if err = cm.storage.EnsureStorageExists(); err != nil {
105
106
107
108
109
110
111
112
113
		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
114
	if cm.secretkey, err = cm.storage.LoadSecretKey(); err != nil {
115
116
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
117
	if cm.attributes, err = cm.storage.LoadAttributes(); err != nil {
118
119
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
120
	if cm.paillierKeyCache, err = cm.storage.LoadPaillierKeys(); err != nil {
121
122
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
123
	if cm.keyshareServers, err = cm.storage.LoadKeyshareServers(); err != nil {
124
125
126
127
128
129
130
131
132
133
		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")
		}
134
		cm.KeyshareEnroll(unenrolled[0], keyshareHandler)
135
136
137
138
139
140
141
	default:
		return nil, errors.New("Too many keyshare servers")
	}

	return cm, nil
}

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

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

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

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

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

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

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

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

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

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

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

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

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

301
302
303
304
305
306
307
308
309
310
311
312
313
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) {
314
315
	cred, _, err := cm.credentialByHash(id.Hash)
	return cred, err
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
		}
369
		creds := cm.attributes[credID]
370
371
372
373
		count := len(creds)
		if count == 0 {
			continue
		}
374
375
		for _, attrs := range creds {
			id := &AttributeIdentifier{Type: attribute, Hash: attrs.hash()}
376
377
378
			if attribute.IsCredential() {
				candidates = append(candidates, id)
			} else {
Sietse Ringers's avatar
Sietse Ringers committed
379
				val := attrs.untranslatedAttribute(attribute)
380
				if val == "" { // This won't handle empty attributes correctly
381
382
383
384
385
386
387
388
389
390
391
392
					continue
				}
				if !disjunction.HasValues() || val == disjunction.Values[attribute] {
					candidates = append(candidates, id)
				}
			}
		}
	}

	return candidates
}

Sietse Ringers's avatar
Sietse Ringers committed
393
394
395
// CheckSatisfiability checks if this credential manager has the required attributes
// to satisfy the specifed disjunction list. If not, the unsatisfiable disjunctions
// are returned.
396
397
398
399
400
401
402
403
404
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 {
405
406
407
			missing = append(missing, disjunction)
		}
	}
408
	return candidates, missing
409
}
410

411
func (cm *CredentialManager) groupCredentials(choice *DisclosureChoice) (map[CredentialIdentifier][]int, error) {
412
	grouped := make(map[CredentialIdentifier][]int)
413
414
415
	if choice == nil || choice.Attributes == nil {
		return grouped, nil
	}
416
417
418
419
420
421
422
423

	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 {
424
425
			indices := make([]int, 1, 1)
			indices[0] = 1 // Always include metadata
426
427
428
429
430
431
			grouped[ici] = indices
		}

		if identifier.IsCredential() {
			continue // In this case we only disclose the metadata attribute, which is already handled
		}
432
		index, err := cm.ConfigurationStore.CredentialTypes[identifier.CredentialTypeIdentifier()].IndexOf(identifier)
433
434
435
436
		if err != nil {
			return nil, err
		}

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

	return grouped, nil
}

445
// ProofBuilders constructs a list of proof builders for the specified attribute choice.
446
func (cm *CredentialManager) ProofBuilders(choice *DisclosureChoice) (gabi.ProofBuilderList, error) {
447
448
449
450
451
	todisclose, err := cm.groupCredentials(choice)
	if err != nil {
		return nil, err
	}

452
	builders := gabi.ProofBuilderList([]gabi.ProofBuilder{})
453
	for id, list := range todisclose {
Sietse Ringers's avatar
Sietse Ringers committed
454
		cred, err := cm.credentialByID(id)
455
456
457
458
459
		if err != nil {
			return nil, err
		}
		builders = append(builders, cred.Credential.CreateDisclosureProofBuilder(list))
	}
Sietse Ringers's avatar
Sietse Ringers committed
460
	return builders, nil
461
}
Sietse Ringers's avatar
Sietse Ringers committed
462

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

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

481
	proofBuilders := gabi.ProofBuilderList([]gabi.ProofBuilder{})
Sietse Ringers's avatar
Sietse Ringers committed
482
	for _, futurecred := range request.Credentials {
Sietse Ringers's avatar
Sietse Ringers committed
483
		var pk *gabi.PublicKey
484
		pk, err = cm.ConfigurationStore.PublicKey(futurecred.CredentialTypeID.IssuerIdentifier(), futurecred.KeyCounter)
485
486
487
		if err != nil {
			return nil, err
		}
488
489
		credBuilder := gabi.NewCredentialBuilder(
			pk, request.GetContext(), cm.secretkey.Key, state.nonce2)
Sietse Ringers's avatar
Sietse Ringers committed
490
491
492
		request.state.builders = append(request.state.builders, credBuilder)
		proofBuilders = append(proofBuilders, credBuilder)
	}
Sietse Ringers's avatar
Sietse Ringers committed
493

Sietse Ringers's avatar
Sietse Ringers committed
494
	disclosures, err := cm.ProofBuilders(request.choice)
Sietse Ringers's avatar
Sietse Ringers committed
495
496
497
	if err != nil {
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
498
	proofBuilders = append(disclosures, proofBuilders...)
Sietse Ringers's avatar
Sietse Ringers committed
499
500
	return proofBuilders, nil
}
Sietse Ringers's avatar
Sietse Ringers committed
501

Sietse Ringers's avatar
Sietse Ringers committed
502
503
504
505
506
507
508
// 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
	}
509
	list := proofBuilders.BuildProofList(request.GetContext(), request.GetNonce(), false)
Sietse Ringers's avatar
Sietse Ringers committed
510
	return &gabi.IssueCommitmentMessage{Proofs: list, Nonce2: request.state.nonce2}, nil
Sietse Ringers's avatar
Sietse Ringers committed
511
512
}

Sietse Ringers's avatar
Sietse Ringers committed
513
514
// ConstructCredentials constructs and saves new credentials
// using the specified issuance signature messages.
Sietse Ringers's avatar
Sietse Ringers committed
515
516
517
518
519
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")
	}

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

535
	for _, gabicred := range gabicreds {
536
		newcred, err := newCredential(gabicred, cm.ConfigurationStore)
537
538
539
		if err != nil {
			return err
		}
Sietse Ringers's avatar
Sietse Ringers committed
540
541
542
		if err = cm.addCredential(newcred, true); err != nil {
			return err
		}
Sietse Ringers's avatar
Sietse Ringers committed
543
	}
544

Sietse Ringers's avatar
Sietse Ringers committed
545
	return nil
Sietse Ringers's avatar
Sietse Ringers committed
546
}
547

Sietse Ringers's avatar
Sietse Ringers committed
548
549
// Keyshare server handling

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

func (cm *CredentialManager) unenrolledKeyshareServers() []*SchemeManager {
	list := []*SchemeManager{}
569
	for name, manager := range cm.ConfigurationStore.SchemeManagers {
Sietse Ringers's avatar
Sietse Ringers committed
570
571
572
573
574
575
		if _, contains := cm.keyshareServers[name]; len(manager.KeyshareServer) > 0 && !contains {
			list = append(list, manager)
		}
	}
	return list
}
Sietse Ringers's avatar
Sietse Ringers committed
576

577
// KeyshareEnroll attempts to register at the keyshare server of the specified scheme manager.
578
579
580
581
582
583
584
585
586
587
588
589
590
591
func (cm *CredentialManager) KeyshareEnroll(manager *SchemeManager, handler KeyshareHandler) {
	handler.StartRegistration(manager, func(email, pin string) {
		go func() {
			err := cm.keyshareEnrollWorker(manager.Identifier(), email, pin)
			if err != nil {
				handler.RegistrationError(err)
			} else {
				handler.RegistrationSuccess()
			}
		}()
	})
}

func (cm *CredentialManager) keyshareEnrollWorker(managerID SchemeManagerIdentifier, email, pin string) error {
592
	manager, ok := cm.ConfigurationStore.SchemeManagers[managerID]
Sietse Ringers's avatar
Sietse Ringers committed
593
594
595
596
597
598
599
600
601
602
603
	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)
604
	kss, err := newKeyshareServer(cm.paillierKey(true), manager.KeyshareServer, email)
Sietse Ringers's avatar
Sietse Ringers committed
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
	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
	}

620
	cm.keyshareServers[managerID] = kss
Sietse Ringers's avatar
Sietse Ringers committed
621
	return cm.storage.StoreKeyshareServers(cm.keyshareServers)
Sietse Ringers's avatar
Sietse Ringers committed
622
623
}

624
// KeyshareRemove unregisters the keyshare server of the specified scheme manager.
Sietse Ringers's avatar
Sietse Ringers committed
625
626
627
628
629
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
630
	return cm.storage.StoreKeyshareServers(cm.keyshareServers)
Sietse Ringers's avatar
Sietse Ringers committed
631
}
Sietse Ringers's avatar
Sietse Ringers committed
632

Sietse Ringers's avatar
Sietse Ringers committed
633
634
// Add, load and store log entries

635
func (cm *CredentialManager) addLogEntry(entry *LogEntry) error {
Sietse Ringers's avatar
Sietse Ringers committed
636
	cm.logs = append(cm.logs, entry)
637
	return cm.storage.StoreLogs(cm.logs)
638
	return nil
Sietse Ringers's avatar
Sietse Ringers committed
639
640
641
642
643
}

func (cm *CredentialManager) Logs() ([]*LogEntry, error) {
	if cm.logs == nil || len(cm.logs) == 0 {
		var err error
Sietse Ringers's avatar
Sietse Ringers committed
644
		cm.logs, err = cm.storage.LoadLogs()
Sietse Ringers's avatar
Sietse Ringers committed
645
646
647
648
649
650
		if err != nil {
			return nil, err
		}
	}
	return cm.logs, nil
}