storage.go 5.62 KB
Newer Older
1
package irmaclient
2
3
4
5
6

import (
	"encoding/json"
	"io/ioutil"
	"os"
7

8
9
	"github.com/credentials/irmago"
	"github.com/credentials/irmago/internal/fs"
10
	"github.com/mhe/gabi"
11
12
)

Sietse Ringers's avatar
Sietse Ringers committed
13
14
15
// This file contains the storage struct and its methods,
// and some general filesystem functions.

16
// Storage provider for a Client
17
type storage struct {
18
19
	storagePath   string
	Configuration *irma.Configuration
20
21
}

22
23
24
25
// Filenames in which we store stuff
const (
	skFile         = "sk"
	attributesFile = "attrs"
26
27
	kssFile        = "kss"
	paillierFile   = "paillier"
28
	updatesFile    = "updates"
Sietse Ringers's avatar
Sietse Ringers committed
29
	logsFile       = "logs"
30
31
32
	signaturesDir  = "sigs"
)

33
34
func (s *storage) path(p string) string {
	return s.storagePath + "/" + p
35
36
}

Sietse Ringers's avatar
Sietse Ringers committed
37
// EnsureStorageExists initializes the credential storage folder,
Sietse Ringers's avatar
Sietse Ringers committed
38
39
40
41
// ensuring that it is in a usable state.
// NOTE: we do not create the folder if it does not exist!
// Setting it up in a properly protected location (e.g., with automatic
// backups to iCloud/Google disabled) is the responsibility of the user.
Sietse Ringers's avatar
Sietse Ringers committed
42
func (s *storage) EnsureStorageExists() error {
43
	if err := fs.AssertPathExists(s.storagePath); err != nil {
44
45
		return err
	}
46
	return fs.EnsureDirectoryExists(s.path(signaturesDir))
47
48
}

Sietse Ringers's avatar
Sietse Ringers committed
49
func (s *storage) load(dest interface{}, path string) (err error) {
50
	exists, err := fs.PathExists(s.path(path))
Sietse Ringers's avatar
Sietse Ringers committed
51
52
53
54
	if err != nil || !exists {
		return
	}
	bytes, err := ioutil.ReadFile(s.path(path))
55
	if err != nil {
Sietse Ringers's avatar
Sietse Ringers committed
56
		return
57
	}
Sietse Ringers's avatar
Sietse Ringers committed
58
	return json.Unmarshal(bytes, dest)
59
60
}

Sietse Ringers's avatar
Sietse Ringers committed
61
62
func (s *storage) store(contents interface{}, file string) error {
	bts, err := json.Marshal(contents)
63
64
65
	if err != nil {
		return err
	}
66
	return fs.SaveFile(s.path(file), bts)
Sietse Ringers's avatar
Sietse Ringers committed
67
68
}

69
func (s *storage) signatureFilename(attrs *irma.AttributeList) string {
Sietse Ringers's avatar
Sietse Ringers committed
70
71
72
73
74
	// We take the SHA256 hash over all attributes as the filename for the signature.
	// This means that the signatures of two credentials that have identical attributes
	// will be written to the same file, one overwriting the other - but that doesn't
	// matter, because either one of the signatures is valid over both attribute lists,
	// so keeping one of them suffices.
75
	return signaturesDir + "/" + attrs.Hash()
Sietse Ringers's avatar
Sietse Ringers committed
76
77
}

78
func (s *storage) DeleteSignature(attrs *irma.AttributeList) error {
Sietse Ringers's avatar
Sietse Ringers committed
79
80
	return os.Remove(s.path(s.signatureFilename(attrs)))
}
81

Sietse Ringers's avatar
Sietse Ringers committed
82
83
func (s *storage) StoreSignature(cred *credential) error {
	return s.store(cred.Signature, s.signatureFilename(cred.AttributeList()))
84
85
}

Sietse Ringers's avatar
Sietse Ringers committed
86
87
88
89
func (s *storage) StoreSecretKey(sk *secretKey) error {
	return s.store(sk, skFile)
}

90
91
func (s *storage) StoreAttributes(attributes map[irma.CredentialTypeIdentifier][]*irma.AttributeList) error {
	temp := []*irma.AttributeList{}
92
	for _, attrlistlist := range attributes {
93
94
95
		for _, attrlist := range attrlistlist {
			temp = append(temp, attrlist)
		}
96
97
	}

Sietse Ringers's avatar
Sietse Ringers committed
98
	return s.store(temp, attributesFile)
99
}
Sietse Ringers's avatar
Sietse Ringers committed
100

101
func (s *storage) StoreKeyshareServers(keyshareServers map[irma.SchemeManagerIdentifier]*keyshareServer) (err error) {
Sietse Ringers's avatar
Sietse Ringers committed
102
	return s.store(keyshareServers, kssFile)
103
104
}

Sietse Ringers's avatar
Sietse Ringers committed
105
106
func (s *storage) StorePaillierKeys(key *paillierPrivateKey) (err error) {
	return s.store(key, paillierFile)
107
108
}

Sietse Ringers's avatar
Sietse Ringers committed
109
110
func (s *storage) StoreLogs(logs []*LogEntry) (err error) {
	return s.store(logs, logsFile)
Sietse Ringers's avatar
Sietse Ringers committed
111
112
}

Sietse Ringers's avatar
Sietse Ringers committed
113
114
func (s *storage) StoreUpdates(updates []update) (err error) {
	return s.store(updates, updatesFile)
115
116
}

117
func (s *storage) LoadSignature(attrs *irma.AttributeList) (signature *gabi.CLSignature, err error) {
118
	sigpath := s.signatureFilename(attrs)
119
	if err := fs.AssertPathExists(s.path(sigpath)); err != nil {
120
		return nil, err
121
	}
Sietse Ringers's avatar
Sietse Ringers committed
122
	signature = new(gabi.CLSignature)
Sietse Ringers's avatar
Sietse Ringers committed
123
124
125
126
	if err := s.load(signature, sigpath); err != nil {
		return nil, err
	}
	return signature, nil
Sietse Ringers's avatar
Sietse Ringers committed
127
128
}

Sietse Ringers's avatar
Sietse Ringers committed
129
// LoadSecretKey retrieves and returns the secret key from storage, or if no secret key
Sietse Ringers's avatar
Sietse Ringers committed
130
// was found in storage, it generates, saves, and returns a new secret key.
Sietse Ringers's avatar
Sietse Ringers committed
131
func (s *storage) LoadSecretKey() (*secretKey, error) {
132
	var err error
Sietse Ringers's avatar
Sietse Ringers committed
133
134
	sk := &secretKey{}
	if err = s.load(sk, skFile); err != nil {
Sietse Ringers's avatar
Sietse Ringers committed
135
136
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
137
138
	if sk.Key != nil {
		return sk, nil
Sietse Ringers's avatar
Sietse Ringers committed
139
	}
140

Sietse Ringers's avatar
Sietse Ringers committed
141
	if sk, err = generateSecretKey(); err != nil {
142
143
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
144
	if err = s.StoreSecretKey(sk); err != nil {
145
146
147
		return nil, err
	}
	return sk, nil
Sietse Ringers's avatar
Sietse Ringers committed
148
149
}

150
func (s *storage) LoadAttributes() (list map[irma.CredentialTypeIdentifier][]*irma.AttributeList, err error) {
Sietse Ringers's avatar
Sietse Ringers committed
151
	// The attributes are stored as a list of instances of AttributeList
152
	temp := []*irma.AttributeList{}
Sietse Ringers's avatar
Sietse Ringers committed
153
	if err = s.load(&temp, attributesFile); err != nil {
Sietse Ringers's avatar
Sietse Ringers committed
154
155
		return
	}
156

157
	list = make(map[irma.CredentialTypeIdentifier][]*irma.AttributeList)
158
	for _, attrlist := range temp {
159
		attrlist.MetadataAttribute = irma.MetadataFromInt(attrlist.Ints[0], s.Configuration)
160
		id := attrlist.CredentialType()
161
		var ct irma.CredentialTypeIdentifier
162
163
164
165
		if id != nil {
			ct = id.Identifier()
		}
		if _, contains := list[ct]; !contains {
166
			list[ct] = []*irma.AttributeList{}
167
		}
168
		list[ct] = append(list[ct], attrlist)
169
170
	}

171
	return list, nil
Sietse Ringers's avatar
Sietse Ringers committed
172
}
173

174
175
func (s *storage) LoadKeyshareServers() (ksses map[irma.SchemeManagerIdentifier]*keyshareServer, err error) {
	ksses = make(map[irma.SchemeManagerIdentifier]*keyshareServer)
Sietse Ringers's avatar
Sietse Ringers committed
176
	if err := s.load(&ksses, kssFile); err != nil {
177
178
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
179
	return ksses, nil
180
181
}

Sietse Ringers's avatar
Sietse Ringers committed
182
func (s *storage) LoadPaillierKeys() (key *paillierPrivateKey, err error) {
183
	key = new(paillierPrivateKey)
Sietse Ringers's avatar
Sietse Ringers committed
184
	if err := s.load(key, paillierFile); err != nil {
185
186
		return nil, err
	}
187
188
189
	if key.N == nil { // TODO this is ugly
		return nil, nil
	}
Sietse Ringers's avatar
Sietse Ringers committed
190
	return key, nil
191
}
Sietse Ringers's avatar
Sietse Ringers committed
192

Sietse Ringers's avatar
Sietse Ringers committed
193
func (s *storage) LoadLogs() (logs []*LogEntry, err error) {
Sietse Ringers's avatar
Sietse Ringers committed
194
	logs = []*LogEntry{}
Sietse Ringers's avatar
Sietse Ringers committed
195
	if err := s.load(&logs, logsFile); err != nil {
Sietse Ringers's avatar
Sietse Ringers committed
196
197
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
198
	return logs, nil
Sietse Ringers's avatar
Sietse Ringers committed
199
}
200

Sietse Ringers's avatar
Sietse Ringers committed
201
func (s *storage) LoadUpdates() (updates []update, err error) {
202
	updates = []update{}
Sietse Ringers's avatar
Sietse Ringers committed
203
	if err := s.load(&updates, updatesFile); err != nil {
204
205
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
206
	return updates, nil
207
}