Skip to content

Commit

Permalink
Config: Fix config version downgrade
Browse files Browse the repository at this point in the history
  • Loading branch information
gbjk committed Jan 9, 2025
1 parent fcd78ad commit 9f3442a
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 112 deletions.
114 changes: 47 additions & 67 deletions cmd/config/config.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package main

import (
"errors"
"context"
"flag"
"fmt"
"os"
Expand All @@ -11,9 +11,10 @@ import (
"github.com/buger/jsonparser"
"github.com/thrasher-corp/gocryptotrader/common/file"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/config/versions"
)

var commands = []string{"upgrade", "encrypt", "decrypt"}
var commands = []string{"upgrade", "downgrade", "encrypt", "decrypt"}

func main() {
fmt.Println("GoCryptoTrader: config-helper tool")
Expand All @@ -22,13 +23,15 @@ func main() {

var in, out, keyStr string
var inplace bool
var version int

fs := flag.NewFlagSet("config", flag.ExitOnError)
fs.Usage = func() { usage(fs) }
fs.StringVar(&in, "in", defaultCfgFile, "The config input file to process")
fs.StringVar(&out, "out", "[in].out", "The config output file")
fs.BoolVar(&inplace, "edit", false, "Edit; Save result to the original file")
fs.StringVar(&keyStr, "key", "", "The key to use for AES encryption")
fs.IntVar(&version, "version", 0, "The version to downgrade to")

cmd, args := parseCommand(os.Args[1:])
if cmd == "" {
Expand All @@ -46,83 +49,59 @@ func main() {
out = in + ".out"
}

key := []byte(keyStr)
var err error
switch cmd {
case "upgrade":
err = upgradeFile(in, out, key)
case "decrypt":
err = encryptWrapper(in, out, key, false, decryptFile)
case "encrypt":
err = encryptWrapper(in, out, key, true, encryptFile)
}
key := []byte(keyStr)
data := readFile(in)
isEncrypted := config.IsEncrypted(data)

if err != nil {
fatal(err.Error())
if cmd == "encrypt" && isEncrypted {
fatal("Error: File is already encrypted")
}

fmt.Println("Success! File written to " + out)
}

func upgradeFile(in, out string, key []byte) error {
c := &config.Config{
EncryptionKeyProvider: func(_ bool) ([]byte, error) {
if len(key) != 0 {
return key, nil
}
return config.PromptForConfigKey(false)
},
if len(key) == 0 && (isEncrypted || cmd == "encrypt") {
if key, err = config.PromptForConfigKey(cmd == "encrypt"); err != nil {
fatal(err.Error())
}
}

if err := c.ReadConfigFromFile(in, true); err != nil {
return err
if isEncrypted {
if data, err = config.DecryptConfigData(data, key); err != nil {
fatal(err.Error())
}
}

return c.SaveConfigToFile(out)
}

type encryptFunc func(string, []byte) ([]byte, error)

func encryptWrapper(in, out string, key []byte, confirmKey bool, fn encryptFunc) error {
if len(key) == 0 {
var err error
if key, err = config.PromptForConfigKey(confirmKey); err != nil {
return err
switch cmd {
case "decrypt":
if data, err = jsonparser.Set(data, []byte("-1"), "encryptConfig"); err != nil {
fatal("Unable to decrypt config data; Error: " + err.Error())
}
case "downgrade", "upgrade":
if version == 0 {
if cmd == "downgrade" {
fmt.Fprintln(os.Stderr, "Error: downgrade requires a version")
usage(fs)
os.Exit(3)
}
version = -1
}
if data, err = versions.Manager.Deploy(context.Background(), data, version); err != nil {
fatal("Unable to " + cmd + " config; Error: " + err.Error())
}
if !isEncrypted {
break
}
fallthrough
case "encrypt":
if data, err = config.EncryptConfigData(data, key); err != nil {
fatal("Unable to encrypt config data; Error: " + err.Error())
}
}
outData, err := fn(in, key)
if err != nil {
return err
}
if err := file.Write(out, outData); err != nil {
return fmt.Errorf("unable to write output file %s; Error: %w", out, err)
}
return nil
}

func encryptFile(in string, key []byte) ([]byte, error) {
if config.IsFileEncrypted(in) {
return nil, errors.New("file is already encrypted")
}
outData, err := config.EncryptConfigFile(readFile(in), key)
if err != nil {
return nil, fmt.Errorf("unable to encrypt config data. Error: %w", err)
if err := file.Write(out, data); err != nil {
fatal("Unable to write output file `" + out + "`; Error: " + err.Error())
}
return outData, nil
}

func decryptFile(in string, key []byte) ([]byte, error) {
if !config.IsFileEncrypted(in) {
return nil, errors.New("file is already decrypted")
}
outData, err := config.DecryptConfigFile(readFile(in), key)
if err != nil {
return nil, fmt.Errorf("unable to decrypt config data. Error: %w", err)
}
if outData, err = jsonparser.Set(outData, []byte("-1"), "encryptConfig"); err != nil {
return nil, fmt.Errorf("unable to decrypt config data. Error: %w", err)
}
return outData, nil
fmt.Println("Success! File written to " + out)
}

func readFile(in string) []byte {
Expand Down Expand Up @@ -152,7 +131,7 @@ func parseCommand(a []string) (cmd string, args []string) {
switch len(cmds) {
case 0:
fmt.Fprintln(os.Stderr, "No command provided")
case 1: //
case 1:
return cmds[0], rem
default:
fmt.Fprintln(os.Stderr, "Too many commands provided: "+strings.Join(cmds, ", "))
Expand All @@ -171,6 +150,7 @@ The commands are:
encrypt encrypt infile and write to outfile
decrypt decrypt infile and write to outfile
upgrade upgrade the version of a decrypted config file
downgrade downgrade the version of a decrypted config file to a specific version
The arguments are:`)
fs.PrintDefaults()
Expand Down
6 changes: 3 additions & 3 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -1512,7 +1512,7 @@ func (c *Config) readConfig(d io.Reader) error {
}
}

if j, err = versions.Manager.Deploy(context.Background(), j); err != nil {
if j, err = versions.Manager.Deploy(context.Background(), j, -1); err != nil {
return err
}

Expand Down Expand Up @@ -1546,7 +1546,7 @@ func (c *Config) decryptConfig(j []byte) ([]byte, error) {
log.Errorf(log.ConfigMgr, "PromptForConfigKey err: %s", err)
continue
}
d, err := c.decryptConfigData(j, key)
d, err := c.DecryptConfigData(j, key)
if err != nil {
log.Errorln(log.ConfigMgr, "Could not decrypt and deserialise data with given key. Invalid password?", err)
continue
Expand Down Expand Up @@ -1603,7 +1603,7 @@ func (c *Config) Save(writerProvider func() (io.Writer, error)) error {
}
c.sessionDK, c.storedSalt = sessionDK, storedSalt
}
payload, err = c.encryptConfigFile(payload)
payload, err = c.encryptConfigData(payload)
if err != nil {
return err
}
Expand Down
20 changes: 10 additions & 10 deletions config/config_encryption.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ func getSensitiveInput(prompt string) (resp []byte, err error) {
return bytes.TrimRight(resp, "\r\n"), err
}

// EncryptConfigFile encrypts json config data with a key
func EncryptConfigFile(configData, key []byte) ([]byte, error) {
// EncryptConfigData encrypts json config data with a key
func EncryptConfigData(configData, key []byte) ([]byte, error) {
sessionDK, salt, err := makeNewSessionDK(key)
if err != nil {
return nil, err
Expand All @@ -105,12 +105,12 @@ func EncryptConfigFile(configData, key []byte) ([]byte, error) {
sessionDK: sessionDK,
storedSalt: salt,
}
return c.encryptConfigFile(configData)
return c.encryptConfigData(configData)
}

// encryptConfigFile encrypts json config data with a key
// encryptConfigData encrypts json config data with a key
// The EncryptConfig field is set to config enabled (1)
func (c *Config) encryptConfigFile(configData []byte) ([]byte, error) {
func (c *Config) encryptConfigData(configData []byte) ([]byte, error) {
configData, err := jsonparser.Set(configData, []byte("1"), "encryptConfig")
if err != nil {
return nil, fmt.Errorf("%w: %w", ErrSettingEncryptConfig, err)
Expand All @@ -135,13 +135,13 @@ func (c *Config) encryptConfigFile(configData []byte) ([]byte, error) {
return appendedFile, nil
}

// DecryptConfigFile decrypts config data with a key
func DecryptConfigFile(d, key []byte) ([]byte, error) {
return (&Config{}).decryptConfigData(d, key)
// DecryptConfigData decrypts config data with a key
func DecryptConfigData(d, key []byte) ([]byte, error) {
return (&Config{}).DecryptConfigData(d, key)
}

// decryptConfigData decrypts config data with a key
func (c *Config) decryptConfigData(d, key []byte) ([]byte, error) {
// DecryptConfigData decrypts config data with a key
func (c *Config) DecryptConfigData(d, key []byte) ([]byte, error) {
if !bytes.HasPrefix(d, encryptionPrefix) {
return d, errNoPrefix
}
Expand Down
18 changes: 9 additions & 9 deletions config/config_encryption_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,16 @@ func TestPromptForConfigKey(t *testing.T) {

func TestEncryptConfigFile(t *testing.T) {
t.Parallel()
_, err := EncryptConfigFile([]byte("test"), nil)
_, err := EncryptConfigData([]byte("test"), nil)
require.ErrorIs(t, err, errKeyIsEmpty)

c := &Config{
sessionDK: []byte("a"),
}
_, err = c.encryptConfigFile([]byte(`test`))
_, err = c.encryptConfigData([]byte(`test`))
require.ErrorIs(t, err, ErrSettingEncryptConfig)

_, err = c.encryptConfigFile([]byte(`{"test":1}`))
_, err = c.encryptConfigData([]byte(`{"test":1}`))
require.Error(t, err)
require.IsType(t, aes.KeySizeError(1), err)

Expand All @@ -79,26 +79,26 @@ func TestEncryptConfigFile(t *testing.T) {
sessionDK: sessDk,
storedSalt: salt,
}
_, err = c.encryptConfigFile([]byte(`{"test":1}`))
_, err = c.encryptConfigData([]byte(`{"test":1}`))
require.NoError(t, err)
}

func TestDecryptConfigFile(t *testing.T) {
t.Parallel()
e, err := EncryptConfigFile([]byte(`{"test":1}`), []byte("key"))
e, err := EncryptConfigData([]byte(`{"test":1}`), []byte("key"))
require.NoError(t, err)

d, err := DecryptConfigFile(e, []byte("key"))
d, err := DecryptConfigData(e, []byte("key"))
require.NoError(t, err)
assert.Equal(t, `{"test":1,"encryptConfig":1}`, string(d), "encryptConfig should be set to 1 after first encryption")

_, err = DecryptConfigFile(e, nil)
_, err = DecryptConfigData(e, nil)
require.ErrorIs(t, err, errKeyIsEmpty)

_, err = DecryptConfigFile([]byte("test"), nil)
_, err = DecryptConfigData([]byte("test"), nil)
require.ErrorIs(t, err, errNoPrefix)

_, err = DecryptConfigFile(encryptionPrefix, []byte("AAAAAAAAAAAAAAAA"))
_, err = DecryptConfigData(encryptionPrefix, []byte("AAAAAAAAAAAAAAAA"))
require.ErrorIs(t, err, errAESBlockSize)
}

Expand Down
Loading

0 comments on commit 9f3442a

Please sign in to comment.