diff --git a/README.md b/README.md index 23457b7..040da70 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ RCW is a cascading symmetric cryptography agent meant to be embedded within Go p It encrypts all data with both AES256-GCM and ChaCha20-Poly1305. Passphrases are securely cached for three minutes and RPC authentication is used to -ensure that only the binary+user responsible for caching the passphrase can retrieve it. +ensure that only the binary+user responsible for caching the passphrase can utilize it. This feature is supported on Linux, FreeBSD, MacOS, and Windows. RCW also features a sanity check to ensure no data loss occurs due to a user entering the diff --git a/daemon/2server.go b/daemon/2server.go index 4f67db1..be19b9b 100644 --- a/daemon/2server.go +++ b/daemon/2server.go @@ -16,15 +16,22 @@ type RCWService struct{} // 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 { +func (h *RCWService) DecryptRequest(encBytes []byte, reply *[]byte) error { var err error - *reply, err = wrappers.Decrypt(request, globalPassphrase) + *reply, err = wrappers.Decrypt(encBytes, globalPassphrase) if err != nil { return err } return nil } +// EncryptRequest is the RPC method that encrypts the incoming data using +// the global passphrase and returns the encrypted data +func (h *RCWService) EncryptRequest(decBytes []byte, reply *[]byte) error { + *reply = wrappers.Encrypt(decBytes, globalPassphrase) + return nil +} + // getFileHash returns the SHA256 hash of the file at the given path. func getFileHash(path string) []byte { file, _ := os.Open(path) diff --git a/daemon/3client.go b/daemon/3client.go index 3c0af31..7ff5449 100644 --- a/daemon/3client.go +++ b/daemon/3client.go @@ -2,34 +2,63 @@ package daemon import ( "log" + "net" "net/rpc" ) -// CallDaemonIfOpen uses the RCW daemon (if one is available) to +// DecryptWithDaemonIfOpen 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 { +func DecryptWithDaemonIfOpen(encBytes []byte) []byte { if daemonIsOpen() { - return call(encBytes) + return getDecFromDaemon(encBytes) } return nil } -// call connects to the RPC server and requests the passphrase. -func call(encBytes []byte) []byte { - // connect to the UNIX domain socket/Windows named pipe - conn := getConn() - defer conn.Close() +// EncryptWithDaemonIfOpen uses the RCW daemon (if one is available) to +// encrypt and return data. If no RCW daemon is accessible, nil is returned. +func EncryptWithDaemonIfOpen(decBytes []byte) []byte { + if daemonIsOpen() { + return getEncFromDaemon(decBytes) + } + return nil +} - // create an RPC client using the connection - client := rpc.NewClient(conn) +// getDecFromDaemon requests the RCW daemon to decrypt the given data. +// It returns the decrypted data. +func getDecFromDaemon(encBytes []byte) []byte { + conn, client := connectToDaemon() + defer conn.Close() defer client.Close() - // request the passphrase from the RPC server - var reply []byte - if err := client.Call("RCWService.DecryptRequest", encBytes, &reply); err != nil { + // request decBytes from the RPC server + var decBytes []byte + if err := client.Call("RCWService.DecryptRequest", encBytes, &decBytes); err != nil { log.Fatalf("Error calling RCWService.DecryptRequest: %v", err) } + return decBytes +} - // return the passphrase - return []byte(reply) +// getEncFromDaemon requests the RCW daemon to encrypt the given data. +// It returns the encrypted data. +func getEncFromDaemon(decBytes []byte) []byte { + conn, client := connectToDaemon() + defer conn.Close() + defer client.Close() + + // request encBytes from the RPC server + var encBytes []byte + if err := client.Call("RCWService.EncryptRequest", decBytes, &encBytes); err != nil { + log.Fatalf("Error calling RCWService.EncryptRequest: %v", err) + } + return encBytes +} + +// connectToDaemon establishes a connection to the RCW daemon. +// It returns the connection and the RPC client. +// The caller is responsible for closing the connection and client. +func connectToDaemon() (net.Conn, *rpc.Client) { + conn := getConn() + client := rpc.NewClient(conn) + return conn, client } diff --git a/example.go b/example.go index e886c81..261df10 100644 --- a/example.go +++ b/example.go @@ -15,7 +15,7 @@ import ( // Usage: // rcw init : Generates the required sanity check file // 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 enc : Encrypts the provided text and outputs the ciphertext to ex-cipher.rcw (attempts to use daemon, falls back to user input for passphrase) // 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: @@ -26,6 +26,7 @@ import ( // RPC password sharing // TODO Enhancements: +// Fix sanity check (should be performed when starting the daemon to ensure the correct passphrase is used) // Standalone cmd: // Usable as symmetric-only GPG replacement @@ -45,7 +46,7 @@ func main() { fmt.Println(err) return } - decBytes := daemon.CallDaemonIfOpen(encBytes) + decBytes := daemon.DecryptWithDaemonIfOpen(encBytes) if decBytes == nil { fmt.Println("No RCW daemon available") passphrase := inputHidden("Enter RCW passphrase:") @@ -69,19 +70,23 @@ func main() { if err != nil { fmt.Println(err) } - } - case 4: - // encrypt data (from cli args) - // rcw enc - err := wrappers.RunSanityCheck(sanityFile, []byte(os.Args[3])) - if err != nil { - fmt.Println(err) + return + } else if os.Args[1] == "enc" { + // encrypt data (using daemon if available) + // rcw enc + decBytes := []byte(os.Args[2]) + encBytes := daemon.EncryptWithDaemonIfOpen(decBytes) + if encBytes == nil { + fmt.Println("No RCW daemon available") + passphrase := inputHidden("Enter RCW passphrase:") + encBytes = wrappers.Encrypt(decBytes, passphrase) + } + os.WriteFile(outputFile, encBytes, 0600) return } - encBytes := wrappers.Encrypt([]byte(os.Args[2]), []byte(os.Args[3])) - os.WriteFile(outputFile, encBytes, 0600) + fallthrough default: - fmt.Println("Usage: rcw [init ] | [enc ] | dec | ") + fmt.Println("Usage: rcw [init ] | [enc ] | dec | ") } } diff --git a/go.mod b/go.mod index 38a8723..8e3f452 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,14 @@ module github.com/rwinkhart/rcw -go 1.24.2 +go 1.24.3 require github.com/rwinkhart/peercred-mini v0.0.0-20250407033241-c09add2eceea 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 + golang.org/x/crypto v0.38.0 + golang.org/x/sys v0.33.0 + golang.org/x/term v0.32.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 a399a49..efc431f 100644 --- a/go.sum +++ b/go.sum @@ -4,7 +4,7 @@ github.com/rwinkhart/peercred-mini v0.0.0-20250407033241-c09add2eceea h1:VE2ti/A github.com/rwinkhart/peercred-mini v0.0.0-20250407033241-c09add2eceea/go.mod h1:t+YkvAdnTKTrg4d469tw3K+GCUzX/Bja4h8yKjSIsGs= github.com/rwinkhart/sys-freebsd-13-xucred v0.32.0 h1:KRbqimv9Eexf3VB2FrRAQ4v2fGGu7gt3ayMgdT0FNao= 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= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=