@@ -2,6 +2,7 @@ package keyloader
2
2
3
3
import (
4
4
"fmt"
5
+ "sort"
5
6
"strconv"
6
7
"sync"
7
8
"sync/atomic"
@@ -202,6 +203,7 @@ func reorderTimestamp(s *configstore.Item) int64 {
202
203
i , err := s .Unmarshaled ()
203
204
if err == nil {
204
205
ret := i .(* KeyConfig ).Timestamp
206
+ // small hack to tiebreak in favor of sealed keys (mostly in case of zero-value timestamp)
205
207
if i .(* KeyConfig ).Sealed {
206
208
ret ++
207
209
}
@@ -218,7 +220,62 @@ func configFactory() interface{} {
218
220
** CONSTRUCTORS
219
221
*/
220
222
223
+ // NewKey returns a symmecrypt.Key object configured from a number of KeyConfig objects.
224
+ // If several KeyConfigs are supplied, the returned Key will be composite.
225
+ // A composite key encrypts with the latest Key (based on timestamp) and decrypts with any of they keys.
226
+ //
227
+ // If the key configuration specifies it is sealed, the key returned will be wrapped by an unseal mechanism.
228
+ // When the symmecrypt/seal global singleton gets unsealed, the key will become usable instantly. It will return errors in the meantime.
229
+ //
230
+ // The key cipher name is expected to match a KeyFactory that got registered through RegisterCipher().
231
+ // Either use a built-in cipher, or make sure to register a proper factory for this cipher.
232
+ // This KeyFactory will be called, either directly or when the symmecrypt/seal global singleton gets unsealed, if applicable.
233
+ func NewKey (cfgs ... * KeyConfig ) (symmecrypt.Key , error ) {
234
+
235
+ if len (cfgs ) == 0 {
236
+ return nil , errors .New ("missing key config" )
237
+ }
238
+
239
+ // sort by timestamp: latest (bigger timestamp) first
240
+ sort .Slice (cfgs , func (i , j int ) bool { return cfgs [i ].Timestamp > cfgs [j ].Timestamp })
241
+
242
+ firstNonSealed := ! cfgs [0 ].Sealed
243
+ comp := symmecrypt.CompositeKey {}
244
+
245
+ for _ , cfg := range cfgs {
246
+
247
+ var ref symmecrypt.Key
248
+ factory , err := symmecrypt .GetKeyFactory (cfg .Cipher )
249
+ if err != nil {
250
+ return nil , err
251
+ }
252
+ if cfg .Sealed {
253
+ // if the first position (used for encryption in composite keys) was not sealed, but other keys used for fallback decryption are sealed
254
+ // it may be an attack to trigger a reencrypt with a key known by the attacker
255
+ if firstNonSealed {
256
+ return nil , errors .New ("DANGER! Detected downgrade to non-sealed encryption key. Non-sealed key has higher priority, this looks malicious. Aborting!" )
257
+ }
258
+ ref = newSealedKey (cfg , factory )
259
+ } else {
260
+ ref , err = factory .NewKey (cfg .Key )
261
+ if err != nil {
262
+ return nil , err
263
+ }
264
+ }
265
+
266
+ comp = append (comp , ref )
267
+ }
268
+
269
+ // if only a single key config was provided, decapsulate the composite key
270
+ if len (comp ) == 1 {
271
+ return comp [0 ], nil
272
+ }
273
+
274
+ return comp , nil
275
+ }
276
+
221
277
// LoadKey instantiates a new encryption key for a given identifier from the default store in configstore.
278
+ // It retrieves all the necessary data from configstore then calls NewKey().
222
279
//
223
280
// If several keys are found for the identifier, they are sorted by timestamp, and a composite key is returned.
224
281
// The most recent key will be used for encryption, and decryption will be done by any of them.
@@ -235,6 +292,7 @@ func LoadKey(identifier string) (symmecrypt.Key, error) {
235
292
}
236
293
237
294
// LoadKeyFromStore instantiates a new encryption key for a given identifier from a specific store instance.
295
+ // It retrieves all the necessary data from configstore then calls NewKey().
238
296
//
239
297
// If several keys are found for the identifier, they are sorted by timestamp, and a composite key is returned.
240
298
// The most recent key will be used for encryption, and decryption will be done by any of them.
@@ -261,52 +319,34 @@ func LoadKeyFromStore(identifier string, store *configstore.Store) (symmecrypt.K
261
319
return nil , fmt .Errorf ("ambiguous config: several encryption keys conflicting for '%s'" , identifier )
262
320
}
263
321
264
- comp := symmecrypt.CompositeKey {}
265
-
266
- hadNonSealed := false
322
+ var cfgs []* KeyConfig
267
323
268
324
for _ , item := range items .Items {
269
-
270
325
i , err := item .Unmarshaled ()
271
326
if err != nil {
272
327
return nil , err
273
328
}
274
- var ref symmecrypt.Key
275
329
cfg := i .(* KeyConfig )
276
- factory , err := symmecrypt .GetKeyFactory (cfg .Cipher )
277
- if err != nil {
278
- return nil , err
279
- }
280
- if cfg .Sealed {
281
- if hadNonSealed {
282
- panic (fmt .Sprintf ("encryption key '%s': DANGER! Detected downgrade to non-sealed encryption key. Non-sealed key has higher priority, this looks malicious. Aborting!" , identifier ))
283
- }
284
- ref = newSealedKey (cfg , factory )
285
- } else {
286
- hadNonSealed = true
287
- ref , err = factory .NewKey (cfg .Key )
288
- if err != nil {
289
- return nil , err
290
- }
291
- }
292
-
293
- comp = append (comp , ref )
330
+ cfgs = append (cfgs , cfg )
294
331
}
295
332
296
- if len (comp ) == 1 {
297
- return comp [0 ], nil
333
+ key , err := NewKey (cfgs ... )
334
+ if err != nil {
335
+ return nil , fmt .Errorf ("encryption key '%s': %s" , identifier , err )
298
336
}
299
337
300
- return comp , nil
338
+ return key , nil
301
339
}
302
340
303
341
// LoadSingleKey instantiates a new encryption key using LoadKey from the default store in configstore without specifying its identifier.
342
+ // It retrieves all the necessary data from configstore then calls NewKey().
304
343
// It will error if several different identifiers are found.
305
344
func LoadSingleKey () (symmecrypt.Key , error ) {
306
345
return LoadSingleKeyFromStore (configstore .DefaultStore )
307
346
}
308
347
309
348
// LoadSingleKey instantiates a new encryption key using LoadKey from a specific store instance without specifying its identifier.
349
+ // It retrieves all the necessary data from configstore then calls NewKey().
310
350
// It will error if several different identifiers are found.
311
351
func LoadSingleKeyFromStore (store * configstore.Store ) (symmecrypt.Key , error ) {
312
352
ident , err := singleKeyIdentifier (store )
0 commit comments