Implemented encryption (#2)
This commit is contained in:
parent
f3cf37bdb0
commit
d78809c9ee
|
@ -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")
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue