Added config-helper tool to encrypt/decrypt config file.

This commit is contained in:
Adrian Gallagher
2017-03-05 20:04:03 +11:00
parent 5347ea8fa9
commit 145c23aa30

View File

@@ -0,0 +1,185 @@
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
)
const (
ENCRYPTION_CONFIRMATION_STRING = "THORS-HAMMER"
)
func EncryptOrDecrypt(encrypt bool) string {
if encrypt {
return "encrypted"
}
return "decrypted"
}
func main() {
var inFile, outFile, key string
var encrypt bool
var err error
flag.StringVar(&inFile, "infile", "config.dat", "The config input file to process.")
flag.StringVar(&outFile, "outfile", "config.dat.out", "The config output file.")
flag.BoolVar(&encrypt, "encrypt", true, "Wether to encrypt or decrypt.")
flag.StringVar(&key, "key", "", "The key to use for AES encryption.")
flag.Parse()
log.Println("GoCryptoTrader: config-helper tool.")
if key == "" {
result, err := PromptForConfigKey()
if err != nil {
log.Fatal("Unable to obtain encryption/decryption key.")
}
key = string(result)
}
file, err := ReadFile(inFile)
if err != nil {
log.Fatalf("Unable to read input file %s. Error: %s.", inFile, err)
}
if ConfirmECS(file) && encrypt {
log.Println("File is already encrypted. Decrypting..")
encrypt = false
}
if !ConfirmECS(file) && !encrypt {
var result interface{}
err := ConfirmConfigJSON(file, result)
if err != nil {
log.Fatal("File isn't in JSON format")
}
log.Println("File is already decrypted. Encrypting..")
encrypt = true
}
var data []byte
if encrypt {
data, err = EncryptConfigFile(file, []byte(key))
if err != nil {
log.Fatalf("Unable to encrypt config data. Error: %s.", err)
}
} else {
data, err = DecryptConfigFile(file, []byte(key))
if err != nil {
log.Fatalf("Unable to decrypt config data. Error: %s.", err)
}
}
err = WriteFile(outFile, data)
if err != nil {
log.Fatalf("Unable to write output file %s. Error: %s", outFile, err)
}
log.Printf("Successfully %s input file %s and wrote output to %s.\n", EncryptOrDecrypt(encrypt), inFile, outFile)
}
func ReadFile(path string) ([]byte, error) {
file, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
return file, nil
}
func WriteFile(file string, data []byte) error {
err := ioutil.WriteFile(file, data, 0644)
if err != nil {
return err
}
return nil
}
func PromptForConfigKey() ([]byte, error) {
var cryptoKey []byte
for len(cryptoKey) != 32 {
log.Println("Enter password (32 characters):")
_, err := fmt.Scanln(&cryptoKey)
if err != nil {
return nil, err
}
if len(cryptoKey) > 32 || len(cryptoKey) < 32 {
continue
}
}
nonce := make([]byte, 12)
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
return cryptoKey, nil
}
func EncryptConfigFile(configData, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
ciphertext := make([]byte, aes.BlockSize+len(configData))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(ciphertext[aes.BlockSize:], configData)
appendedFile := []byte(ENCRYPTION_CONFIRMATION_STRING)
appendedFile = append(appendedFile, ciphertext...)
return appendedFile, nil
}
func DecryptConfigFile(configData, key []byte) ([]byte, error) {
configData = RemoveECS(configData)
blockDecrypt, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
if len(configData) < aes.BlockSize {
return nil, errors.New("The config file data is too small for the AES required block size.")
}
iv := configData[:aes.BlockSize]
configData = configData[aes.BlockSize:]
stream := cipher.NewCFBDecrypter(blockDecrypt, iv)
stream.XORKeyStream(configData, configData)
result := configData
return result, nil
}
func ConfirmConfigJSON(file []byte, result interface{}) error {
err := json.Unmarshal(file, &result)
if err != nil {
return err
}
return nil
}
func ConfirmECS(file []byte) bool {
subslice := []byte(ENCRYPTION_CONFIRMATION_STRING)
return bytes.Contains(file, subslice)
}
func RemoveECS(file []byte) []byte {
return bytes.Trim(file, ENCRYPTION_CONFIRMATION_STRING)
}