diff --git a/.gitignore b/.gitignore index 5e424e4..425cabb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ /main /main.exe +/example +/example.exe /rcw /rcw.exe /ex-cipher.rcw -/ex-sanity.rcw +/ex-sanity.rcw \ No newline at end of file diff --git a/daemon/2server.go b/daemon/2server.go index 0790cbe..4f67db1 100644 --- a/daemon/2server.go +++ b/daemon/2server.go @@ -4,18 +4,24 @@ import ( "crypto/sha256" "io" "os" + + "github.com/rwinkhart/rcw/wrappers" ) var daemonHash []byte -var globalPassphrase string +var globalPassphrase []byte // RCWService provides an RPC method. type RCWService struct{} -// GetPass is the RPC method. -// For now (as a test/example), it returns "hello" if the input is "hi". -func (h *RCWService) GetPass(request string, reply *string) error { - *reply = globalPassphrase +// DecryptRequest is the RPC method that decrypts the incoming data using +// the global passphrase and returns the decrypted data +func (h *RCWService) DecryptRequest(request []byte, reply *[]byte) error { + var err error + *reply, err = wrappers.Decrypt(request, globalPassphrase) + if err != nil { + return err + } return nil } diff --git a/daemon/2serverUNIXGeneric.go b/daemon/2serverUNIXGeneric.go index 81d4096..d01f178 100644 --- a/daemon/2serverUNIXGeneric.go +++ b/daemon/2serverUNIXGeneric.go @@ -16,9 +16,10 @@ import ( peercred "github.com/rwinkhart/peercred-mini" ) -// Start should be called to serve the given passphrase through an RPC daemon. -func Start(passphrase string) { - // store passphrase to be referenced by GetPass method +// Start is the entry point for the RPC server responsible for +// returning decrypted data to authenticated clients. +func Start(passphrase []byte) { + // store passphrase to be referenced by DecryptRequest method globalPassphrase = passphrase // register RCWService with the RPC package diff --git a/daemon/2serverWindows.go b/daemon/2serverWindows.go index d0c96ca..622892d 100644 --- a/daemon/2serverWindows.go +++ b/daemon/2serverWindows.go @@ -12,7 +12,7 @@ import ( "syscall" "time" - "github.com/Microsoft/go-winio" // For Windows named pipes + "github.com/Microsoft/go-winio" "github.com/rwinkhart/peercred-mini" "golang.org/x/sys/windows" ) @@ -21,9 +21,10 @@ const ( PROCESS_QUERY_LIMITED_INFORMATION = 0x1000 ) -// Start should be called to serve the given passphrase through an RPC daemon. -func Start(passphrase string) { - // store passphrase to be referenced by GetPass method +// Start is the entry point for the RPC server responsible for +// returning decrypted data to authenticated clients. +func Start(passphrase []byte) { + // store passphrase to be referenced by DecryptRequest method globalPassphrase = passphrase // register RCWService with the RPC package diff --git a/daemon/3client.go b/daemon/3client.go index 603c113..3c0af31 100644 --- a/daemon/3client.go +++ b/daemon/3client.go @@ -5,18 +5,17 @@ import ( "net/rpc" ) -// CallDaemonIfOpen returns the passphrase served by the RCW daemon -// (if one is available). If no RCW daemon is accessible, nil is returned. -func CallDaemonIfOpen() []byte { +// CallDaemonIfOpen uses the RCW daemon (if one is available) to +// decrypt and return data. If no RCW daemon is accessible, nil is returned. +func CallDaemonIfOpen(encBytes []byte) []byte { if daemonIsOpen() { - call() - return call() + return call(encBytes) } return nil } // call connects to the RPC server and requests the passphrase. -func call() []byte { +func call(encBytes []byte) []byte { // connect to the UNIX domain socket/Windows named pipe conn := getConn() defer conn.Close() @@ -26,9 +25,9 @@ func call() []byte { defer client.Close() // request the passphrase from the RPC server - var reply string - if err := client.Call("RCWService.GetPass", "hi", &reply); err != nil { - log.Fatalf("Error calling RCWService.GetPass: %v", err) + var reply []byte + if err := client.Call("RCWService.DecryptRequest", encBytes, &reply); err != nil { + log.Fatalf("Error calling RCWService.DecryptRequest: %v", err) } // return the passphrase diff --git a/example.go b/example.go index e094e07..12c9652 100644 --- a/example.go +++ b/example.go @@ -6,6 +6,7 @@ import ( "github.com/rwinkhart/rcw/daemon" "github.com/rwinkhart/rcw/wrappers" + "golang.org/x/term" ) // This sample program serves purley as a way to interactively test the features @@ -13,10 +14,9 @@ import ( // // Usage: // rcw init : Generates the required sanity check file -// rcw : Runs the rcw daemon to serve the provided text for three minutes -// rcw : Requests the data served by the RCW daemon and outputs it to stdout +// rcw : Runs the rcw daemon to decrypt data for three minutes // rcw enc : Encrypts the provided text and outputs the ciphertext to encrypted-example.txt -// rcw dec : Decrypts encrypted-example.txt and outputs the plaintext to stdout +// rcw dec : Decrypts ex-cipher.rcw and outputs the plaintext to stdout (attempts to use daemon, falls back to user input for passphrase) // TODO Tests: // Salt (aes+chacha) @@ -26,9 +26,6 @@ import ( // RPC password sharing // TODO Enhancements: -// Security: -// Handle decryption in the daemon and return decrypted data, rather than returning the passphrase -// Play with nonce sizes and KDF parameters to find the best speed-security balance // Standalone cmd: // Usable as symmetric-only GPG replacement @@ -40,28 +37,42 @@ const ( func main() { switch len(os.Args) { case 2: - // serve data - daemon.Start(os.Args[1]) + if os.Args[1] == "dec" { + // decrypt file (using daemon if available) + // rcw dec + encBytes, err := os.ReadFile(outputFile) + if err != nil { + fmt.Println(err) + return + } + decBytes := daemon.CallDaemonIfOpen(encBytes) + if decBytes == nil { + fmt.Println("No RCW daemon available") + passphrase := inputHidden("Enter RCW passphrase:") + decBytes, err = wrappers.Decrypt(encBytes, passphrase) + if err != nil { + fmt.Println(err) + return + } + } + fmt.Println(string(decBytes)) + return + } + // run decrypter daemon + // rcw + daemon.Start([]byte(os.Args[1])) case 3: if os.Args[1] == "init" { // create sanity check file + // rcw init err := wrappers.GenSanityCheck(sanityFile, []byte(os.Args[2])) if err != nil { fmt.Println(err) } - return } - - // decrypt file - encBytes, _ := os.ReadFile(outputFile) - decBytes, err := wrappers.Decrypt(encBytes, []byte(os.Args[2])) - if err != nil { - fmt.Println(err) - return - } - fmt.Println(string(decBytes)) case 4: // encrypt data (from cli args) + // rcw enc err := wrappers.RunSanityCheck(sanityFile, []byte(os.Args[3])) if err != nil { fmt.Println(err) @@ -70,11 +81,14 @@ func main() { encBytes := wrappers.Encrypt([]byte(os.Args[2]), []byte(os.Args[3])) os.WriteFile(outputFile, encBytes, 0600) default: - // request served data - if servedData := daemon.CallDaemonIfOpen(); servedData != nil { - fmt.Println(string(servedData)) - } else { - fmt.Println("No RCW daemon available") - } + fmt.Println("Usage: rcw [init ] | [enc ] | [dec] | []") } } + +// inputHidden prompts the user for input and returns the input as a byte array, hiding the input from the terminal. +func inputHidden(prompt string) []byte { + fmt.Print("\n" + prompt + " ") + byteInput, _ := term.ReadPassword(int(os.Stdin.Fd())) + fmt.Println() + return byteInput +} diff --git a/go.mod b/go.mod index 4d05ba1..38a8723 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/Microsoft/go-winio v0.6.2 golang.org/x/crypto v0.37.0 golang.org/x/sys v0.32.0 + golang.org/x/term v0.31.0 ) replace golang.org/x/sys => github.com/rwinkhart/sys-freebsd-13-xucred v0.32.0 diff --git a/go.sum b/go.sum index 8a13cca..a399a49 100644 --- a/go.sum +++ b/go.sum @@ -6,3 +6,5 @@ github.com/rwinkhart/sys-freebsd-13-xucred v0.32.0 h1:KRbqimv9Eexf3VB2FrRAQ4v2fG github.com/rwinkhart/sys-freebsd-13-xucred v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= +golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= diff --git a/wrappers/keyDeriv.go b/wrappers/keyDeriv.go index d6f6bce..48d0de5 100644 --- a/wrappers/keyDeriv.go +++ b/wrappers/keyDeriv.go @@ -9,7 +9,7 @@ import ( const ( // parameters for Argon2 argonTime = 8 // set to pass 1-second test in dev environment - argonMemory = 384 * 1024 // 384 MB + argonMemory = 384 * 1024 // 384 MB (target running comfortably on a Pi Zero/512 MB RAM) // general constants keyLen = 32 // 256 bits, key length for both algorithms