storage.go 6.85 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
)

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

18
19
20
21
22
23
// Storage provider for a CredentialManager
type storage struct {
	storagePath        string
	ConfigurationStore *ConfigurationStore
}

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

35
36
37
38
39
40
41
42
43
44
45
46
47
// 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
48
// PathExists checks if the specified path exists.
49
func PathExists(path string) (bool, error) {
50
51
52
53
54
55
56
57
58
59
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return true, err
}

60
61
62
63
64
65
66
67
68
69
70
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
71
72
73
74
75
76
77
78
79
// 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)
80
	if err != nil {
Sietse Ringers's avatar
Sietse Ringers committed
81
		return
82
	}
Sietse Ringers's avatar
Sietse Ringers committed
83
84
85
86
87
88
	tempfilename := hex.EncodeToString(randBytes)

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

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

95
96
func (s *storage) path(p string) string {
	return s.storagePath + "/" + p
97
98
}

Sietse Ringers's avatar
Sietse Ringers committed
99
// EnsureStorageExists initializes the credential storage folder,
Sietse Ringers's avatar
Sietse Ringers committed
100
101
102
103
// 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
104
func (s *storage) EnsureStorageExists() error {
105
	if err := AssertPathExists(s.storagePath); err != nil {
106
107
		return err
	}
108
	return ensureDirectoryExists(s.path(signaturesDir))
109
110
}

Sietse Ringers's avatar
Sietse Ringers committed
111
112
113
114
115
116
func (s *storage) load(dest interface{}, path string) (err error) {
	exists, err := PathExists(s.path(path))
	if err != nil || !exists {
		return
	}
	bytes, err := ioutil.ReadFile(s.path(path))
117
	if err != nil {
Sietse Ringers's avatar
Sietse Ringers committed
118
		return
119
	}
Sietse Ringers's avatar
Sietse Ringers committed
120
	return json.Unmarshal(bytes, dest)
121
122
}

Sietse Ringers's avatar
Sietse Ringers committed
123
124
func (s *storage) store(contents interface{}, file string) error {
	bts, err := json.Marshal(contents)
125
126
127
	if err != nil {
		return err
	}
Sietse Ringers's avatar
Sietse Ringers committed
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
	return saveFile(s.path(file), bts)
}

func (s *storage) signatureFilename(attrs *AttributeList) string {
	// 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.
	return signaturesDir + "/" + attrs.hash()
}

func (s *storage) DeleteSignature(attrs *AttributeList) error {
	return os.Remove(s.path(s.signatureFilename(attrs)))
}
143

Sietse Ringers's avatar
Sietse Ringers committed
144
145
func (s *storage) StoreSignature(cred *credential) error {
	return s.store(cred.Signature, s.signatureFilename(cred.AttributeList()))
146
147
}

Sietse Ringers's avatar
Sietse Ringers committed
148
149
150
151
152
func (s *storage) StoreSecretKey(sk *secretKey) error {
	return s.store(sk, skFile)
}

func (s *storage) StoreAttributes(attributes map[CredentialTypeIdentifier][]*AttributeList) error {
153
	temp := []*AttributeList{}
154
	for _, attrlistlist := range attributes {
155
156
157
		for _, attrlist := range attrlistlist {
			temp = append(temp, attrlist)
		}
158
159
	}

Sietse Ringers's avatar
Sietse Ringers committed
160
	return s.store(temp, attributesFile)
161
}
Sietse Ringers's avatar
Sietse Ringers committed
162

Sietse Ringers's avatar
Sietse Ringers committed
163
164
func (s *storage) StoreKeyshareServers(keyshareServers map[SchemeManagerIdentifier]*keyshareServer) (err error) {
	return s.store(keyshareServers, kssFile)
165
166
}

Sietse Ringers's avatar
Sietse Ringers committed
167
168
func (s *storage) StorePaillierKeys(key *paillierPrivateKey) (err error) {
	return s.store(key, paillierFile)
169
170
}

Sietse Ringers's avatar
Sietse Ringers committed
171
172
func (s *storage) StoreLogs(logs []*LogEntry) (err error) {
	return s.store(logs, logsFile)
Sietse Ringers's avatar
Sietse Ringers committed
173
174
}

Sietse Ringers's avatar
Sietse Ringers committed
175
176
func (s *storage) StoreUpdates(updates []update) (err error) {
	return s.store(updates, updatesFile)
177
178
}

Sietse Ringers's avatar
Sietse Ringers committed
179
func (s *storage) LoadSignature(attrs *AttributeList) (signature *gabi.CLSignature, err error) {
180
	sigpath := s.signatureFilename(attrs)
Sietse Ringers's avatar
Sietse Ringers committed
181
	if err := AssertPathExists(s.path(sigpath)); err != nil {
182
		return nil, err
183
	}
Sietse Ringers's avatar
Sietse Ringers committed
184
	signature = new(gabi.CLSignature)
Sietse Ringers's avatar
Sietse Ringers committed
185
186
187
188
	if err := s.load(signature, sigpath); err != nil {
		return nil, err
	}
	return signature, nil
Sietse Ringers's avatar
Sietse Ringers committed
189
190
}

Sietse Ringers's avatar
Sietse Ringers committed
191
// LoadSecretKey retrieves and returns the secret key from storage, or if no secret key
Sietse Ringers's avatar
Sietse Ringers committed
192
// was found in storage, it generates, saves, and returns a new secret key.
Sietse Ringers's avatar
Sietse Ringers committed
193
func (s *storage) LoadSecretKey() (*secretKey, error) {
194
	var err error
Sietse Ringers's avatar
Sietse Ringers committed
195
196
	sk := &secretKey{}
	if err = s.load(sk, skFile); err != nil {
Sietse Ringers's avatar
Sietse Ringers committed
197
198
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
199
200
	if sk.Key != nil {
		return sk, nil
Sietse Ringers's avatar
Sietse Ringers committed
201
	}
202

Sietse Ringers's avatar
Sietse Ringers committed
203
	if sk, err = generateSecretKey(); err != nil {
204
205
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
206
	if err = s.StoreSecretKey(sk); err != nil {
207
208
209
		return nil, err
	}
	return sk, nil
Sietse Ringers's avatar
Sietse Ringers committed
210
211
}

Sietse Ringers's avatar
Sietse Ringers committed
212
213
214
215
func (s *storage) LoadAttributes() (list map[CredentialTypeIdentifier][]*AttributeList, err error) {
	// The attributes are stored as a list of instances of AttributeList
	temp := []*AttributeList{}
	if err = s.load(&temp, attributesFile); err != nil {
Sietse Ringers's avatar
Sietse Ringers committed
216
217
		return
	}
218
219
220

	list = make(map[CredentialTypeIdentifier][]*AttributeList)
	for _, attrlist := range temp {
221
		attrlist.MetadataAttribute = MetadataFromInt(attrlist.Ints[0], s.ConfigurationStore)
222
223
224
225
226
227
228
		id := attrlist.CredentialType()
		var ct CredentialTypeIdentifier
		if id != nil {
			ct = id.Identifier()
		}
		if _, contains := list[ct]; !contains {
			list[ct] = []*AttributeList{}
229
		}
230
		list[ct] = append(list[ct], attrlist)
231
232
	}

233
	return list, nil
Sietse Ringers's avatar
Sietse Ringers committed
234
}
235

Sietse Ringers's avatar
Sietse Ringers committed
236
func (s *storage) LoadKeyshareServers() (ksses map[SchemeManagerIdentifier]*keyshareServer, err error) {
237
	ksses = make(map[SchemeManagerIdentifier]*keyshareServer)
Sietse Ringers's avatar
Sietse Ringers committed
238
	if err := s.load(&ksses, kssFile); err != nil {
239
240
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
241
	return ksses, nil
242
243
}

Sietse Ringers's avatar
Sietse Ringers committed
244
func (s *storage) LoadPaillierKeys() (key *paillierPrivateKey, err error) {
245
	key = new(paillierPrivateKey)
Sietse Ringers's avatar
Sietse Ringers committed
246
	if err := s.load(key, paillierFile); err != nil {
247
248
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
249
	return key, nil
250
}
Sietse Ringers's avatar
Sietse Ringers committed
251

Sietse Ringers's avatar
Sietse Ringers committed
252
func (s *storage) LoadLogs() (logs []*LogEntry, err error) {
Sietse Ringers's avatar
Sietse Ringers committed
253
	logs = []*LogEntry{}
Sietse Ringers's avatar
Sietse Ringers committed
254
	if err := s.load(&logs, logsFile); err != nil {
Sietse Ringers's avatar
Sietse Ringers committed
255
256
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
257
	return logs, nil
Sietse Ringers's avatar
Sietse Ringers committed
258
}
259

Sietse Ringers's avatar
Sietse Ringers committed
260
func (s *storage) LoadUpdates() (updates []update, err error) {
261
	updates = []update{}
Sietse Ringers's avatar
Sietse Ringers committed
262
	if err := s.load(&updates, updatesFile); err != nil {
263
264
		return nil, err
	}
Sietse Ringers's avatar
Sietse Ringers committed
265
	return updates, nil
266
}