-
Notifications
You must be signed in to change notification settings - Fork 200
/
crypto.go
83 lines (65 loc) · 2.15 KB
/
crypto.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package utils
import (
"bytes"
"io/ioutil"
"strings"
"sync"
"filippo.io/age"
)
// EncryptionKeys random 32 byte key encoded into base64 string. Used by default for configs
var EncryptionKeys = `/45pB920B6DFNwCB/n4rYUio3AVMawrdtrFnjTSIzL4=`
// decryption takes a bunch of RAM to generate scrypt identity
// we don't do decryption in hot paths so it's better to only allow one thread doing decryption at a time to avoi OOM
var decryptMutex sync.Mutex
const (
encryptionKeyEnvName = `ENCRYPTION_KEYS`
keySeparator = `&`
)
// GetEncryptionKeys returns list of encryption keys from ENCRYPTION_KEYS env variable name or default value
func GetEncryptionKeys() ([]string, error) {
keysString := GetEnvStringDefault(encryptionKeyEnvName, EncryptionKeys)
if keysString != EncryptionKeys {
// if user specified own keys, add default at end to be sure that it always used too
// to avoid manual copy/join default key to new
keysString = keysString + keySeparator + EncryptionKeys
}
// +1 to allocate for case if no separator and list contains key itself
// otherwise we just allocate +1 struct for string slice that stores just 2 int fields
// that is not a lot
output := make([]string, 0, strings.Count(keysString, keySeparator)+1)
for _, key := range strings.Split(keysString, keySeparator) {
if key != "" {
output = append(output, key)
}
}
return output, nil
}
// IsEncrypted returns true if cfg encrypted with age tool (https://github.com/FiloSottile/age)
func IsEncrypted(cfg []byte) bool {
return bytes.Contains(cfg, []byte(`age-encryption`))
}
// Decrypt decrypts config using EncryptionKeys
func Decrypt(cfg []byte) ([]byte, error) {
keys, err := GetEncryptionKeys()
if err != nil {
return nil, err
}
decryptMutex.Lock()
defer decryptMutex.Unlock()
var lastErr error
// iterate over all keys and return on first success decryption
for _, key := range keys {
identity, err := age.NewScryptIdentity(key)
if err != nil {
lastErr = err
continue
}
decryptedReader, err := age.Decrypt(bytes.NewReader(cfg), identity)
if err != nil {
lastErr = err
continue
}
return ioutil.ReadAll(decryptedReader)
}
return nil, lastErr
}