diff --git a/src/calanonsync/settings.go b/src/calanonsync/settings.go index 4e11ffd..3b64a92 100644 --- a/src/calanonsync/settings.go +++ b/src/calanonsync/settings.go @@ -10,6 +10,7 @@ import ( "github.com/spf13/cobra" "golang.org/x/crypto/ssh/terminal" "io" + "io/ioutil" "log" "os" "strings" @@ -33,22 +34,6 @@ type Settings struct { const settingsName = "calanonsync.json" const keyName = ".calanonsync.key" -func ensurePassword(password *string, name string) { - if *password != "" { - // Nothing to do. Password already set. - return - } - - print(name + " password: ") - b, err := terminal.ReadPassword(int(syscall.Stdin)) - println() - if err != nil { - panic(err) - } - - *password = string(b) -} - func LoadSettings() Settings { f, err := os.Open(settingsName) if err != nil { @@ -64,6 +49,55 @@ func LoadSettings() Settings { settings.CalDAV.URL += "/" } + // Load a key if possible. + var key []byte = nil + keyString, err := ioutil.ReadFile(keyName) + if err == nil { + key, err = base64.StdEncoding.DecodeString(string(keyString)) + if err != nil { + log.Fatalf("Could not load encryption key: %s\n", err) + } + } else if !os.IsNotExist(err) { + log.Fatalf("Could not load encryption key: %s\n", err) + } + + ensurePassword := func(password *string, name string) { + if *password == "" { + print(name + " password: ") + b, err := terminal.ReadPassword(int(syscall.Stdin)) + println() + if err != nil { + panic(err) + } + + *password = string(b) + } else if key != nil { + // Password already set. Since we have an encryption key, try to + // decrypt the password. + pwbytes, err := base64.StdEncoding.DecodeString(*password) + if err != nil { + log.Fatalf("Could not decode password: %s\n", err) + } + + block, err := aes.NewCipher(key) + if err != nil { + log.Fatalf("Could not create cipher: %s\n", err) + } + + if len(pwbytes) < block.BlockSize() { + log.Fatalln("Could not decrypt password. Encrypted stream is too short.") + } + + iv := pwbytes[:aes.BlockSize] + result := pwbytes[aes.BlockSize:] + + stream := cipher.NewCFBDecrypter(block, iv) + stream.XORKeyStream(result, result) + + *password = string(result) + } + } + ensurePassword(&settings.EWS.Password, "EWS") ensurePassword(&settings.CalDAV.Password, "CalDAV")