Implemented encryption (#2)

This commit is contained in:
Andreas Schneider 2018-04-06 20:09:52 +02:00
parent f3cf37bdb0
commit d78809c9ee
1 changed files with 80 additions and 7 deletions

View File

@ -1,10 +1,16 @@
package main package main
import ( import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"golang.org/x/crypto/ssh/terminal" "golang.org/x/crypto/ssh/terminal"
"io"
"log"
"os" "os"
"strings" "strings"
"syscall" "syscall"
@ -24,6 +30,9 @@ type Settings struct {
} }
} }
const settingsName = "calanonsync.json"
const keyName = ".calanonsync.key"
func ensurePassword(password *string, name string) { func ensurePassword(password *string, name string) {
if *password != "" { if *password != "" {
// Nothing to do. Password already set. // Nothing to do. Password already set.
@ -41,7 +50,7 @@ func ensurePassword(password *string, name string) {
} }
func LoadSettings() Settings { func LoadSettings() Settings {
f, err := os.Open("calanonsync.json") f, err := os.Open(settingsName)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -74,19 +83,83 @@ func InitSettingsCmd() *cobra.Command {
not empty. It will generate a new "master" password and store that alongside not empty. It will generate a new "master" password and store that alongside
the settings file. This is NOT secure, it just helps to prevent the settings file. This is NOT secure, it just helps to prevent
over-the-shoulder "attacks".`, over-the-shoulder "attacks".`,
Run: func(cmd *cobra.Command, args []string) { Run: runSettingsEncryption,
fmt.Println("Encrypt")
},
} }
decryptCmd := &cobra.Command{ decryptCmd := &cobra.Command{
Use: "decrypt", Use: "decrypt",
Short: "Decrypt a previously encrypted settings file.", Short: "Decrypt a previously encrypted settings file.",
Run: func(cmd *cobra.Command, args []string) { Run: runSettingsDecryption,
fmt.Println("Decrypt")
},
} }
settingsCmd.AddCommand(encryptCmd, decryptCmd) settingsCmd.AddCommand(encryptCmd, decryptCmd)
return settingsCmd return settingsCmd
} }
func runSettingsEncryption(cmd *cobra.Command, args []string) {
s := LoadSettings()
if _, err := os.Stat(keyName); err == nil || os.IsExist(err) {
log.Fatalln("Cannot encrypt an (apparently) already encrypted settings file. If this is an error, please remove .calanonsync.key and try again.")
}
// Generate a secure 256 bit key.
key := make([]byte, 32)
if n, err := rand.Read(key); n != 32 || err != nil {
log.Fatalf("Could not get random 256 bit key: %s (%d)\n", err, n)
}
block, err := aes.NewCipher(key)
if err != nil {
log.Fatalf("Could not create cipher: %s\n", err)
}
doEncrypt := func(pwd *string) {
if *pwd != "" {
result := make([]byte, aes.BlockSize+len(*pwd))
// Prepare the initialization vector
iv := result[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(result[aes.BlockSize:], []byte(*pwd))
*pwd = base64.StdEncoding.EncodeToString(result)
}
}
doEncrypt(&s.EWS.Password)
doEncrypt(&s.CalDAV.Password)
if s.EWS.Password == "" && s.CalDAV.Password == "" {
log.Fatalf("No passwords found. Nothing to encrypt.")
}
// Rewrite the settings file.
f, err := os.OpenFile(settingsName, os.O_WRONLY|os.O_TRUNC, 0600)
if err != nil {
log.Fatalf("Could not rewrite settings: %s\n", err)
}
defer f.Close()
e := json.NewEncoder(f)
e.SetIndent("", " ")
err = e.Encode(&s)
if err != nil {
panic(err)
}
f, err = os.OpenFile(keyName, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
if err != nil {
log.Fatalf("Could not write keyfile: %s\n", err)
}
defer f.Close()
ks := base64.StdEncoding.EncodeToString(key)
_, err = f.WriteString(ks)
if err != nil {
panic(err)
}
}
func runSettingsDecryption(cmd *cobra.Command, args []string) {
fmt.Println("Decrypt")
}