10
"github.com/ProtonMail/go-crypto/openpgp"
11
"github.com/pkg/errors"
12
"github.com/wal-g/wal-g/internal/crypto"
13
"github.com/wal-g/wal-g/internal/ioextensions"
16
// Crypter incapsulates specific of cypher method
17
// Includes keys, infrastructure information etc
18
// If many encryption methods will be used it worth
19
// to extract interface
28
IsUseArmoredKeyPath bool
30
PubKey openpgp.EntityList
31
SecretKey openpgp.EntityList
33
loadPassphrase func() (string, bool)
38
func (crypter *Crypter) Name() string {
39
return "Opengpg/Crypter"
42
// CrypterFromKey creates Crypter from armored key.
43
func CrypterFromKey(armoredKey string, loadPassphrase func() (string, bool)) crypto.Crypter {
44
return &Crypter{ArmoredKey: armoredKey, IsUseArmoredKey: true, loadPassphrase: loadPassphrase}
47
// CrypterFromKeyPath creates Crypter from armored key path.
48
func CrypterFromKeyPath(armoredKeyPath string, loadPassphrase func() (string, bool)) crypto.Crypter {
49
return &Crypter{ArmoredKeyPath: armoredKeyPath, IsUseArmoredKeyPath: true, loadPassphrase: loadPassphrase}
52
// CrypterFromKeyRingID create Crypter from key ring ID.
53
func CrypterFromKeyRingID(keyRingID string, loadPassphrase func() (string, bool)) crypto.Crypter {
54
return &Crypter{KeyRingID: keyRingID, IsUseKeyRingID: true, loadPassphrase: loadPassphrase}
57
func (crypter *Crypter) setupPubKey() error {
59
if crypter.PubKey != nil {
60
crypter.mutex.RUnlock()
63
crypter.mutex.RUnlock()
66
defer crypter.mutex.Unlock()
67
if crypter.PubKey != nil { // already set up
72
case crypter.IsUseArmoredKey:
73
evaluatedKey := strings.Replace(crypter.ArmoredKey, `\n`, "\n", -1)
74
entityList, err := openpgp.ReadArmoredKeyRing(strings.NewReader(evaluatedKey))
80
crypter.PubKey = entityList
82
case crypter.IsUseArmoredKeyPath:
83
entityList, err := readPGPKey(crypter.ArmoredKeyPath)
89
crypter.PubKey = entityList
92
// TODO: legacy gpg external use, need to remove in next major version
93
armor, err := crypto.GetPubRingArmor(crypter.KeyRingID)
99
entityList, err := openpgp.ReadArmoredKeyRing(bytes.NewReader(armor))
105
crypter.PubKey = entityList
110
// Encrypt creates encryption writer from ordinary writer
111
func (crypter *Crypter) Encrypt(writer io.Writer) (io.WriteCloser, error) {
112
err := crypter.setupPubKey()
117
// We use buffered writer because encryption starts writing header immediately,
118
// which can be inappropriate for further usage with blocking writers.
119
// E. g. if underlying writer is a pipe, then this thread will be blocked before
120
// creation of new thread, reading from this pipe.Writer.
121
bufferedWriter := bufio.NewWriter(writer)
122
encryptedWriter, err := openpgp.Encrypt(bufferedWriter, crypter.PubKey, nil, nil, nil)
125
return nil, errors.Wrapf(err, "opengpg encryption error")
128
return ioextensions.NewOnCloseFlusher(encryptedWriter, bufferedWriter), nil
131
// Decrypt creates decrypted reader from ordinary reader
132
func (crypter *Crypter) Decrypt(reader io.Reader) (io.Reader, error) {
133
err := crypter.loadSecret()
139
md, err := openpgp.ReadMessage(reader, crypter.SecretKey, nil, nil)
142
return nil, errors.WithStack(err)
145
return md.UnverifiedBody, nil
148
// load the secret key based on the settings
149
func (crypter *Crypter) loadSecret() error {
150
// check if we actually need to load it
151
crypter.mutex.RLock()
152
if crypter.SecretKey != nil {
153
crypter.mutex.RUnlock()
156
// unlock needs to be there twice due to different code paths
157
crypter.mutex.RUnlock()
159
// we need to load, so lock for writing
161
defer crypter.mutex.Unlock()
163
// double check as the key might have been loaded between the RUnlock and Lock
164
if crypter.SecretKey != nil {
168
if crypter.IsUseArmoredKey {
169
evaluatedKey := strings.Replace(crypter.ArmoredKey, `\n`, "\n", -1)
170
entityList, err := openpgp.ReadArmoredKeyRing(strings.NewReader(evaluatedKey))
173
return errors.WithStack(err)
176
crypter.SecretKey = entityList
177
} else if crypter.IsUseArmoredKeyPath {
178
entityList, err := readPGPKey(crypter.ArmoredKeyPath)
181
return errors.WithStack(err)
184
crypter.SecretKey = entityList
186
// TODO: legacy gpg external use, need to remove in next major version
187
armor, err := crypto.GetSecretRingArmor(crypter.KeyRingID)
190
return errors.WithStack(err)
193
entityList, err := openpgp.ReadArmoredKeyRing(bytes.NewReader(armor))
196
return errors.WithStack(err)
199
crypter.SecretKey = entityList
202
if passphrase, ok := crypter.loadPassphrase(); ok {
203
err := decryptSecretKey(crypter.SecretKey, passphrase)
206
return errors.WithStack(err)