Skip to content

Commit 0d66d1a

Browse files
authored
keyloader: Support loading keys from separate store instances (#19)
* keyloader: Support loading keys from separate store instances * Typos in comments * seal: Parameter order * Misc fixes
1 parent 838657f commit 0d66d1a

File tree

4 files changed

+95
-31
lines changed

4 files changed

+95
-31
lines changed

ciphers/hmac/hmac.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,25 @@ func (f hmacFactory) NewRandomKey() (symmecrypt.Key, error) {
5353
// Key is a simple key which uses plain data + HMAC-sha512 for authentication
5454
type Key []byte
5555

56-
func tag(h hash.Hash, data, s []byte) []byte {
56+
func tag(h hash.Hash, data, s []byte) ([]byte, error) {
5757
al := make([]byte, dataLenSize)
5858
binary.BigEndian.PutUint64(al, uint64(len(data)*8)) // in bits
59-
h.Write(data)
60-
h.Write(s)
61-
h.Write(al)
59+
_, err := h.Write(data)
60+
if err != nil {
61+
return nil, err
62+
}
63+
_, err = h.Write(s)
64+
if err != nil {
65+
return nil, err
66+
}
67+
_, err = h.Write(al)
68+
if err != nil {
69+
return nil, err
70+
}
6271
sum := h.Sum(nil)[:rawTagSize]
6372
ret := make([]byte, tagSize)
6473
base64.URLEncoding.Encode(ret, sum)
65-
return ret
74+
return ret, nil
6675
}
6776

6877
// Encrypt appends a base64 HMAC-sha512 (with fully printable characters) calculated from the plaintext + extra data, to the plaintext.
@@ -73,7 +82,10 @@ func (k Key) Encrypt(plain []byte, extra ...[]byte) ([]byte, error) {
7382
extraData = append(extraData, e...)
7483
}
7584

76-
t := tag(hmac.New(sha512.New, []byte(k)), extraData, plain)
85+
t, err := tag(hmac.New(sha512.New, []byte(k)), extraData, plain)
86+
if err != nil {
87+
return nil, err
88+
}
7789

7890
return append(plain, t...), nil
7991
}
@@ -93,7 +105,10 @@ func (k Key) Decrypt(data []byte, extra ...[]byte) ([]byte, error) {
93105
extraData = append(extraData, e...)
94106
}
95107

96-
t2 := tag(hmac.New(sha512.New, []byte(k)), extraData, plain)
108+
t2, err := tag(hmac.New(sha512.New, []byte(k)), extraData, plain)
109+
if err != nil {
110+
return nil, err
111+
}
97112

98113
if !hmac.Equal(t, t2) {
99114
return nil, errors.New("message authentication failed")

keyloader/keyloader.go

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ type watchKey struct {
6666

6767
// A sealedKey is an implementation of an encryption key that is encrypted using symmecrypt/seal.
6868
type sealedKey struct {
69-
encryptedKey string
7069
decryptedKey symmecrypt.Key
7170
decrypted uint32
7271
waitCh chan struct{}
@@ -157,13 +156,19 @@ func UnsealKey(k *KeyConfig, s *seal.Seal) (*KeyConfig, error) {
157156
}, nil
158157
}
159158

160-
// ConfiguredKeys returns a list of all the encryption keys present in the configstore
159+
// ConfiguredKeys returns a list of all the encryption keys present in the default store in configstore
161160
// ensuring they are unsealed.
162161
func ConfiguredKeys() ([]*KeyConfig, error) {
162+
return ConfiguredKeysFromStore(configstore.DefaultStore)
163+
}
164+
165+
// ConfiguredKeys returns a list of all the encryption keys present in a specific store instance
166+
// ensuring they are unsealed.
167+
func ConfiguredKeysFromStore(store *configstore.Store) ([]*KeyConfig, error) {
163168

164169
ret := []*KeyConfig{}
165170

166-
items, err := ConfigFilter.GetItemList()
171+
items, err := ConfigFilter.Store(store).GetItemList()
167172
if err != nil {
168173
return nil, err
169174
}
@@ -213,7 +218,7 @@ func configFactory() interface{} {
213218
** CONSTRUCTORS
214219
*/
215220

216-
// LoadKey instantiates a new encryption key for a given identifier from the configstore.
221+
// LoadKey instantiates a new encryption key for a given identifier from the default store in configstore.
217222
//
218223
// If several keys are found for the identifier, they are sorted by timestamp, and a composite key is returned.
219224
// The most recent key will be used for encryption, and decryption will be done by any of them.
@@ -226,8 +231,24 @@ func configFactory() interface{} {
226231
// Either use a built-in cipher, or make sure to register a proper factory for this cipher.
227232
// This KeyFactory will be called, either directly or when the symmecrypt/seal global singleton gets unsealed, if applicable.
228233
func LoadKey(identifier string) (symmecrypt.Key, error) {
234+
return LoadKeyFromStore(identifier, configstore.DefaultStore)
235+
}
229236

230-
items, err := ConfigFilter.Slice(identifier).GetItemList()
237+
// LoadKeyFromStore instantiates a new encryption key for a given identifier from a specific store instance.
238+
//
239+
// If several keys are found for the identifier, they are sorted by timestamp, and a composite key is returned.
240+
// The most recent key will be used for encryption, and decryption will be done by any of them.
241+
// There needs to be _only one_ key with the highest priority for the identifier.
242+
//
243+
// If the key configuration specifies it is sealed, the key returned will be wrapped by an unseal mechanism.
244+
// When the symmecrypt/seal global singleton gets unsealed, the key will become usable instantly. It will return errors in the meantime.
245+
//
246+
// The key cipher name is expected to match a KeyFactory that got registered through RegisterCipher().
247+
// Either use a built-in cipher, or make sure to register a proper factory for this cipher.
248+
// This KeyFactory will be called, either directly or when the symmecrypt/seal global singleton gets unsealed, if applicable.
249+
func LoadKeyFromStore(identifier string, store *configstore.Store) (symmecrypt.Key, error) {
250+
251+
items, err := ConfigFilter.Slice(identifier).Store(store).GetItemList()
231252
if err != nil {
232253
return nil, err
233254
}
@@ -279,18 +300,24 @@ func LoadKey(identifier string) (symmecrypt.Key, error) {
279300
return comp, nil
280301
}
281302

282-
// LoadSingleKey instantiates a new encryption key using LoadKey from the configstore without specifying its identifier.
303+
// LoadSingleKey instantiates a new encryption key using LoadKey from the default store in configstore without specifying its identifier.
283304
// It will error if several different identifiers are found.
284305
func LoadSingleKey() (symmecrypt.Key, error) {
285-
ident, err := singleKeyIdentifier()
306+
return LoadSingleKeyFromStore(configstore.DefaultStore)
307+
}
308+
309+
// LoadSingleKey instantiates a new encryption key using LoadKey from a specific store instance without specifying its identifier.
310+
// It will error if several different identifiers are found.
311+
func LoadSingleKeyFromStore(store *configstore.Store) (symmecrypt.Key, error) {
312+
ident, err := singleKeyIdentifier(store)
286313
if err != nil {
287314
return nil, err
288315
}
289-
return LoadKey(ident)
316+
return LoadKeyFromStore(ident, store)
290317
}
291318

292-
func singleKeyIdentifier() (string, error) {
293-
items, err := ConfigFilter.GetItemList()
319+
func singleKeyIdentifier(store *configstore.Store) (string, error) {
320+
items, err := ConfigFilter.Store(store).GetItemList()
294321
if err != nil {
295322
return "", err
296323
}
@@ -306,40 +333,52 @@ func singleKeyIdentifier() (string, error) {
306333
return "", errors.New("ambiguous config: several encryption keys found and no identifier supplied")
307334
}
308335

309-
// WatchKey instantiates a new hot-reloading encryption key from the configstore.
336+
// WatchKey instantiates a new hot-reloading encryption key from the default store in configstore.
310337
// It uses LoadKey(), so the underlying implementation can be anything supported (composite, sealed, any cipher, ...)
311338
func WatchKey(identifier string) (symmecrypt.Key, error) {
312-
b, err := LoadKey(identifier)
339+
return WatchKeyFromStore(identifier, configstore.DefaultStore)
340+
}
341+
342+
// WatchKeyFromStore instantiates a new hot-reloading encryption key from a specific store instance.
343+
// It uses LoadKey(), so the underlying implementation can be anything supported (composite, sealed, any cipher, ...)
344+
func WatchKeyFromStore(identifier string, store *configstore.Store) (symmecrypt.Key, error) {
345+
b, err := LoadKeyFromStore(identifier, store)
313346
if err != nil {
314347
return nil, err
315348
}
316349

317350
holder := &watchKey{identifier: identifier, k: b}
318-
go holder.watch()
351+
go holder.watch(store)
319352

320353
return holder, nil
321354
}
322355

323-
// WatchSingleKey instantiates a new hot-relating encryption key from the configstore without specifying its identifier.
356+
// WatchSingleKey instantiates a new hot-reloading encryption key from the default store in configstore without specifying its identifier.
324357
// It will error if several different identifiers are found.
325358
func WatchSingleKey() (symmecrypt.Key, error) {
326-
ident, err := singleKeyIdentifier()
359+
return WatchSingleKeyFromStore(configstore.DefaultStore)
360+
}
361+
362+
// WatchSingleKey instantiates a new hot-reloading encryption key from a specific store instance without specifying its identifier.
363+
// It will error if several different identifiers are found.
364+
func WatchSingleKeyFromStore(store *configstore.Store) (symmecrypt.Key, error) {
365+
ident, err := singleKeyIdentifier(store)
327366
if err != nil {
328367
return nil, err
329368
}
330-
return WatchKey(ident)
369+
return WatchKeyFromStore(ident, store)
331370
}
332371

333372
/*
334373
** WATCH implementation: self updating encryption keys
335374
*/
336375

337376
// Watch for configstore update notifications, then reload the key through LoadKey().
338-
func (kh *watchKey) watch() {
339-
for range configstore.Watch() {
377+
func (kh *watchKey) watch(store *configstore.Store) {
378+
for range store.Watch() {
340379
time.Sleep(10 * time.Millisecond)
341380
// small sleep to yield to symmecrypt/seal in case of seal change
342-
b, err := LoadKey(kh.identifier)
381+
b, err := LoadKeyFromStore(kh.identifier, store)
343382
if err != nil {
344383
logrus.Errorf("symmecrypt/keyloader: configuration fetch error for key '%s': %s", kh.identifier, err)
345384
continue

seal/seal.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,12 @@ type internalKey struct {
5656

5757
// InitFromConfig initializes the global singleton seal from the configstore.
5858
func InitFromConfig(onChange func(*Seal)) error {
59-
seal, err := NewSealFromConfig()
59+
return InitFromStore(onChange, configstore.DefaultStore)
60+
}
61+
62+
// InitFromStore initializes the global singleton seal from a specific store instance.
63+
func InitFromStore(onChange func(*Seal), s *configstore.Store) error {
64+
seal, err := NewSealFromStore(s)
6065
if err != nil {
6166
return err
6267
}
@@ -65,8 +70,8 @@ func InitFromConfig(onChange func(*Seal)) error {
6570
setGlobal(seal)
6671
}
6772
go func() {
68-
for range configstore.Watch() {
69-
newSeal, err := NewSealFromConfig()
73+
for range s.Watch() {
74+
newSeal, err := NewSealFromStore(s)
7075
if err != nil {
7176
logrus.Errorf("symmecrypt/seal: configuration fetch error: %s", err)
7277
continue
@@ -113,7 +118,12 @@ func Exists() bool {
113118

114119
// NewSealFromConfig instantiates a new Seal from the configstore.
115120
func NewSealFromConfig() (*Seal, error) {
116-
sec, err := SealConfigFilter.GetItem(ConfigName)
121+
return NewSealFromStore(configstore.DefaultStore)
122+
}
123+
124+
// NewSealFromStore instantiates a new Seal from a specific store instance.
125+
func NewSealFromStore(s *configstore.Store) (*Seal, error) {
126+
sec, err := SealConfigFilter.Store(s).GetItem(ConfigName)
117127
if err != nil {
118128
if _, ok := err.(configstore.ErrItemNotFound); ok {
119129
// Not found in config: disabled

symmecrypt_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func TestMain(m *testing.M) {
4343

4444
configstore.RegisterProvider("test", ProviderTest)
4545

46-
m.Run()
46+
os.Exit(m.Run())
4747
}
4848

4949
func TestEncryptDecrypt(t *testing.T) {

0 commit comments

Comments
 (0)