storage.go 8.11 KB
Newer Older
1
2
3
package irmago

import (
Sietse Ringers's avatar
Sietse Ringers committed
4
5
	"crypto/rand"
	"encoding/hex"
6
7
8
	"encoding/json"
	"io/ioutil"
	"os"
Sietse Ringers's avatar
Sietse Ringers committed
9
	"path"
10

Sietse Ringers's avatar
Sietse Ringers committed
11
	"github.com/go-errors/errors"
12
	"github.com/mhe/gabi"
13
14
)

15
16
17
18
19
20
// Storage provider for a CredentialManager
type storage struct {
	storagePath        string
	ConfigurationStore *ConfigurationStore
}

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

32
33
34
35
36
37
38
39
40
41
42
43
44
// AssertPathExists returns nil only if it has been successfully
// verified that the specified path exists.
func AssertPathExists(path string) error {
	exist, err := PathExists(path)
	if err != nil {
		return err
	}
	if !exist {
		return errors.Errorf("Path %s does not exist", path)
	}
	return nil
}

Sietse Ringers's avatar
Sietse Ringers committed
45
// PathExists checks if the specified path exists.
46
func PathExists(path string) (bool, error) {
47
48
49
50
51
52
53
54
55
56
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return true, err
}

57
58
59
60
61
62
63
64
65
66
67
func ensureDirectoryExists(path string) error {
	exists, err := PathExists(path)
	if err != nil {
		return err
	}
	if exists {
		return nil
	}
	return os.Mkdir(path, 0700)
}

Sietse Ringers's avatar
Sietse Ringers committed
68
69
70
71
72
73
74
75
76
// Save the filecontents at the specified path atomically:
// - first save the content in a temp file with a random filename in the same dir
// - then rename the temp file to the specified filepath, overwriting the old file
func saveFile(filepath string, content []byte) (err error) {
	dir := path.Dir(filepath)

	// Read random data for filename and convert to hex
	randBytes := make([]byte, 16)
	_, err = rand.Read(randBytes)
77
	if err != nil {
Sietse Ringers's avatar
Sietse Ringers committed
78
		return
79
	}
Sietse Ringers's avatar
Sietse Ringers committed
80
81
82
83
84
85
	tempfilename := hex.EncodeToString(randBytes)

	// Create temp file
	err = ioutil.WriteFile(dir+"/"+tempfilename, content, 0600)
	if err != nil {
		return
86
	}
Sietse Ringers's avatar
Sietse Ringers committed
87
88
89

	// Rename, overwriting old file
	return os.Rename(dir+"/"+tempfilename, filepath)
90
91
}

92
93
func generateSecretKey() (*secretKey, error) {
	key, err := gabi.RandomBigInt(gabi.DefaultSystemParameters[1024].Lm)
Sietse Ringers's avatar
Sietse Ringers committed
94
95
96
	if err != nil {
		return nil, err
	}
97
	return &secretKey{Key: key}, nil
98
99
}

100
101
func (s *storage) path(p string) string {
	return s.storagePath + "/" + p
102
103
}

104
func (s *storage) signatureFilename(attrs *AttributeList) string {
Sietse Ringers's avatar
Sietse Ringers committed
105
106
107
108
109
	// 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.
110
	return s.path(signaturesDir) + "/" + attrs.hash()
Sietse Ringers's avatar
Sietse Ringers committed
111
112
113
114
115
116
117
}

// ensureStorageExists initializes the credential storage folder,
// 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.
118
119
func (s *storage) ensureStorageExists() error {
	if err := AssertPathExists(s.storagePath); err != nil {
120
121
		return err
	}
122
	return ensureDirectoryExists(s.path(signaturesDir))
123
124
}

125
func (s *storage) storeSecretKey(sk *secretKey) error {
126
127
128
129
	bytes, err := json.Marshal(sk)
	if err != nil {
		return err
	}
130
	return saveFile(s.path(skFile), bytes)
131
132
}

133
func (s *storage) storeSignature(cred *credential) (err error) {
134
135
136
137
138
139
140
141
142
	if cred.CredentialType() == nil {
		return errors.New("cannot add unknown credential type")
	}

	credbytes, err := json.Marshal(cred.Signature)
	if err != nil {
		return err
	}

143
	filename := s.signatureFilename(cred.AttributeList())
Sietse Ringers's avatar
Sietse Ringers committed
144
	err = saveFile(filename, credbytes)
145
146
147
	return
}

148
func (s *storage) storeAttributes(attributes map[CredentialTypeIdentifier][]*AttributeList) error {
149
	temp := []*AttributeList{}
150
	for _, attrlistlist := range attributes {
151
152
153
		for _, attrlist := range attrlistlist {
			temp = append(temp, attrlist)
		}
154
155
	}

156
	if attrbytes, err := json.Marshal(temp); err == nil {
157
		return saveFile(s.path(attributesFile), attrbytes)
158
159
160
	} else {
		return err
	}
161
}
Sietse Ringers's avatar
Sietse Ringers committed
162

163
164
func (s *storage) storeKeyshareServers(keyshareServers map[SchemeManagerIdentifier]*keyshareServer) (err error) {
	bts, err := json.Marshal(keyshareServers)
165
166
167
	if err != nil {
		return
	}
168
	err = saveFile(s.path(kssFile), bts)
169
170
171
	return
}

172
173
func (s *storage) storePaillierKeys(key *paillierPrivateKey) (err error) {
	bts, err := json.Marshal(key)
174
175
176
	if err != nil {
		return
	}
177
	err = saveFile(s.path(paillierFile), bts)
178
179
180
	return
}

181
182
func (s *storage) storeLogs(logs []*LogEntry) (err error) {
	bts, err := json.Marshal(logs)
Sietse Ringers's avatar
Sietse Ringers committed
183
184
185
	if err != nil {
		return
	}
186
	err = saveFile(s.path(logsFile), bts)
Sietse Ringers's avatar
Sietse Ringers committed
187
188
189
	return
}

190
191
func (s *storage) storeUpdates(updates []update) (err error) {
	bts, err := json.Marshal(updates)
192
	if err != nil {
Sietse Ringers's avatar
Sietse Ringers committed
193
194
		return
	}
195
196
197
198
199
200
201
202
	err = saveFile(s.path(updatesFile), bts)
	return
}

func (s *storage) loadSignature(attrs *AttributeList) (signature *gabi.CLSignature, err error) {
	sigpath := s.signatureFilename(attrs)
	if err := AssertPathExists(sigpath); err != nil {
		return nil, err
203
	}
Sietse Ringers's avatar
Cleanup    
Sietse Ringers committed
204
	bytes, err := ioutil.ReadFile(sigpath)
205
206
207
	if err != nil {
		return
	}
Sietse Ringers's avatar
Sietse Ringers committed
208
209
210
211
212
213
214
	signature = new(gabi.CLSignature)
	err = json.Unmarshal(bytes, signature)
	return
}

// loadSecretKey retrieves and returns the secret key from storage, or if no secret key
// was found in storage, it generates, saves, and returns a new secret key.
215
func (s *storage) loadSecretKey() (*secretKey, error) {
216
217
	sk := &secretKey{}
	var err error
218
	exists, err := PathExists(s.path(skFile))
Sietse Ringers's avatar
Sietse Ringers committed
219
220
221
222
	if err != nil {
		return nil, err
	}
	if exists {
223
		var bytes []byte
224
		if bytes, err = ioutil.ReadFile(s.path(skFile)); err != nil {
225
			return nil, err
Sietse Ringers's avatar
Sietse Ringers committed
226
		}
227
228
229
230
		if err = json.Unmarshal(bytes, sk); err != nil {
			return nil, err
		}
		return sk, err
Sietse Ringers's avatar
Sietse Ringers committed
231
	}
232

233
	sk, err = generateSecretKey()
234
235
236
	if err != nil {
		return nil, err
	}
237
	err = s.storeSecretKey(sk)
238
239
240
241
	if err != nil {
		return nil, err
	}
	return sk, nil
Sietse Ringers's avatar
Sietse Ringers committed
242
243
}

244
245
func (s *storage) loadAttributes() (list map[CredentialTypeIdentifier][]*AttributeList, err error) {
	exists, err := PathExists(s.path(attributesFile))
Sietse Ringers's avatar
Sietse Ringers committed
246
247
248
	if err != nil || !exists {
		return
	}
249
	bytes, err := ioutil.ReadFile(s.path(attributesFile))
Sietse Ringers's avatar
Sietse Ringers committed
250
251
252
	if err != nil {
		return nil, err
	}
253
254
255
256
257

	// The attributes are stored as a list of instances of AttributeList
	temp := []*AttributeList{}
	list = make(map[CredentialTypeIdentifier][]*AttributeList)
	if err = json.Unmarshal(bytes, &temp); err != nil {
258
259
		return nil, err
	}
260
	for _, attrlist := range temp {
261
		attrlist.MetadataAttribute = MetadataFromInt(attrlist.Ints[0], s.ConfigurationStore)
262
263
264
265
266
267
268
		id := attrlist.CredentialType()
		var ct CredentialTypeIdentifier
		if id != nil {
			ct = id.Identifier()
		}
		if _, contains := list[ct]; !contains {
			list[ct] = []*AttributeList{}
269
		}
270
		list[ct] = append(list[ct], attrlist)
271
272
	}

273
	return list, nil
Sietse Ringers's avatar
Sietse Ringers committed
274
}
275

276
func (s *storage) loadKeyshareServers() (ksses map[SchemeManagerIdentifier]*keyshareServer, err error) {
277
	ksses = make(map[SchemeManagerIdentifier]*keyshareServer)
278
	exists, err := PathExists(s.path(kssFile))
279
280
281
	if err != nil || !exists {
		return
	}
282
	bytes, err := ioutil.ReadFile(s.path(kssFile))
283
284
285
	if err != nil {
		return nil, err
	}
286
	err = json.Unmarshal(bytes, &ksses)
287
288
289
290
291
292
	if err != nil {
		return nil, err
	}
	return
}

293
294
func (s *storage) loadPaillierKeys() (key *paillierPrivateKey, err error) {
	exists, err := PathExists(s.path(paillierFile))
295
296
297
	if err != nil || !exists {
		return
	}
298
	bytes, err := ioutil.ReadFile(s.path(paillierFile))
299
300
301
302
303
304
305
306
307
308
	if err != nil {
		return nil, err
	}
	key = new(paillierPrivateKey)
	err = json.Unmarshal(bytes, key)
	if err != nil {
		return nil, err
	}
	return
}
Sietse Ringers's avatar
Sietse Ringers committed
309

310
func (s *storage) loadLogs() (logs []*LogEntry, err error) {
Sietse Ringers's avatar
Sietse Ringers committed
311
	logs = []*LogEntry{}
312
	exists, err := PathExists(s.path(logsFile))
Sietse Ringers's avatar
Sietse Ringers committed
313
314
315
	if err != nil || !exists {
		return
	}
316
	bytes, err := ioutil.ReadFile(s.path(logsFile))
Sietse Ringers's avatar
Sietse Ringers committed
317
318
319
320
321
322
323
324
325
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(bytes, &logs)
	if err != nil {
		return nil, err
	}
	return
}
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342

func (s *storage) loadUpdates() (updates []update, err error) {
	updates = []update{}
	exists, err := PathExists(s.path(updatesFile))
	if err != nil || !exists {
		return
	}
	bytes, err := ioutil.ReadFile(s.path(updatesFile))
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(bytes, &updates)
	if err != nil {
		return nil, err
	}
	return
}