mirror of
https://github.com/rwinkhart/rcw.git
synced 2026-08-27 20:36:30 -04:00
Make input zeroization optional
This commit is contained in:
+4
-4
@@ -20,17 +20,17 @@ type RCWService struct{}
|
||||
// the global password and returns the decrypted data
|
||||
func (h *RCWService) DecryptRequest(encBytes []byte, reply *[]byte) error {
|
||||
var err error
|
||||
*reply, err = wrappers.DecryptAndZeroizePassword(encBytes, append([]byte{}, globalPassword...)) // pass new slice to avoid zeroizing cached password)
|
||||
*reply, err = wrappers.Decrypt(encBytes, globalPassword, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncryptRequestAndZeroizeDecBytes is the RPC method that encrypts the incoming data using
|
||||
// EncryptRequest is the RPC method that encrypts the incoming data using
|
||||
// the global password and returns the encrypted data
|
||||
func (h *RCWService) EncryptRequestAndZeroizeDecBytes(decBytes []byte, reply *[]byte) error {
|
||||
*reply = wrappers.EncryptAndZeroizeDecBytesAndPassword(decBytes, append([]byte{}, globalPassword...)) // pass new slice to avoid zeroizing cached password
|
||||
func (h *RCWService) EncryptRequest(decBytes []byte, reply *[]byte) error {
|
||||
*reply = wrappers.Encrypt(decBytes, globalPassword, true, false)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/rwinkhart/go-boilerplate/security"
|
||||
"github.com/rwinkhart/peercred-mini"
|
||||
)
|
||||
|
||||
@@ -44,6 +45,7 @@ func Start(password []byte) {
|
||||
go func() {
|
||||
<-sigChan
|
||||
listener.Close()
|
||||
security.ZeroizeBytes(globalPassword)
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
@@ -56,6 +58,7 @@ func Start(password []byte) {
|
||||
if err.(net.Error).Timeout() {
|
||||
log.Println(strconv.Itoa(Timeout) + " seconds have passed without any connections. Exiting...")
|
||||
listener.Close()
|
||||
security.ZeroizeBytes(globalPassword)
|
||||
os.Exit(0)
|
||||
}
|
||||
log.Printf("Accept error: %v", err)
|
||||
@@ -84,7 +87,7 @@ func handleConn(conn net.Conn, sigChan chan os.Signal) {
|
||||
// invalid client; close the connection w/o a response,
|
||||
// log the client's path, and kill the daemon
|
||||
conn.Close()
|
||||
log.Printf("Request received from invalid client: PID(%d), UID(%s), Path(%s)", ucred.PID, ucred.UID, callingBinPath) // TODO log to file
|
||||
sigChan <- syscall.SIGTERM
|
||||
log.Printf("Request received from invalid client: PID(%d), UID(%s), Path(%s)", ucred.PID, ucred.UID, callingBinPath)
|
||||
sigChan <- syscall.SIGTERM // this zeroizes globalPassword and triggers os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,10 +10,10 @@ import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
"github.com/rwinkhart/go-boilerplate/security"
|
||||
"github.com/rwinkhart/peercred-mini"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
@@ -63,11 +63,13 @@ func Start(password []byte) {
|
||||
case <-timer.C:
|
||||
log.Println(strconv.Itoa(Timeout) + " seconds have passed without any connections. Exiting...")
|
||||
listener.Close()
|
||||
security.ZeroizeBytes(globalPassword)
|
||||
os.Exit(0)
|
||||
case <-killTimer:
|
||||
return
|
||||
case <-sigChan:
|
||||
listener.Close()
|
||||
security.ZeroizeBytes(globalPassword)
|
||||
os.Exit(0)
|
||||
}
|
||||
}()
|
||||
@@ -108,8 +110,7 @@ func handleConn(conn net.Conn, sigChan chan os.Signal) {
|
||||
// invalid client; close the connection w/o a response,
|
||||
// log the client's path, and kill the daemon
|
||||
conn.Close()
|
||||
log.Printf("Request received from invalid client: PID(%d), UID(%s), Path(%s)", ucred.PID, ucred.UID, callingBinPath) // TODO log to file
|
||||
sigChan <- syscall.SIGTERM
|
||||
os.Exit(2)
|
||||
log.Printf("Request received from invalid client: PID(%d), UID(%s), Path(%s)", ucred.PID, ucred.UID, callingBinPath)
|
||||
sigChan <- os.Interrupt // this zeroizes globalPassword and triggers os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
+7
-5
@@ -23,19 +23,21 @@ func GetDec(encBytes []byte) []byte {
|
||||
return decBytes
|
||||
}
|
||||
|
||||
// GetEncAndZeroizeDecBytes requests the RCW daemon to encrypt the given data.
|
||||
// GetEnc requests the RCW daemon to encrypt the given data.
|
||||
// It returns the encrypted data.
|
||||
func GetEncAndZeroizeDecBytes(decBytes []byte) []byte {
|
||||
func GetEnc(decBytes []byte, zeroizeDecBytes bool) []byte {
|
||||
conn, client := connectToDaemon()
|
||||
defer conn.Close()
|
||||
defer client.Close()
|
||||
|
||||
// request encBytes from the RPC server
|
||||
var encBytes []byte
|
||||
err := client.Call("RCWService.EncryptRequestAndZeroizeDecBytes", decBytes, &encBytes)
|
||||
security.ZeroizeBytes(decBytes)
|
||||
err := client.Call("RCWService.EncryptRequest", decBytes, &encBytes)
|
||||
if zeroizeDecBytes {
|
||||
security.ZeroizeBytes(decBytes)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("Error calling RCWService.EncryptRequestAndZeroizeDecBytes: %v", err)
|
||||
log.Fatalf("Error calling RCWService.EncryptRequest: %v", err)
|
||||
}
|
||||
return encBytes
|
||||
}
|
||||
|
||||
+8
-7
@@ -66,7 +66,7 @@ func main() {
|
||||
if daemon.IsOpen() {
|
||||
decBytes = daemon.GetDec(encBytes)
|
||||
} else {
|
||||
decBytes, err = wrappers.DecryptAndZeroizePassword(encBytes, front.InputSecret("Enter RCW password:"))
|
||||
decBytes, err = wrappers.Decrypt(encBytes, front.InputSecret("Enter RCW password:"), true)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
@@ -87,27 +87,28 @@ func main() {
|
||||
}
|
||||
daemon.Start([]byte(os.Args[1]))
|
||||
case 3:
|
||||
if os.Args[1] == "init" {
|
||||
switch os.Args[1] {
|
||||
case "init":
|
||||
// create sanity check file
|
||||
// rcw init <passwd>
|
||||
if err := wrappers.GenSanityCheckAndZeroizePassword(sanityFile, []byte(os.Args[2])); err != nil {
|
||||
if err := wrappers.GenSanityCheck(sanityFile, []byte(os.Args[2]), true); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
return
|
||||
} else if os.Args[1] == "enc" {
|
||||
case "enc":
|
||||
// encrypt data (using daemon if available)
|
||||
// rcw enc <data>
|
||||
decBytes := []byte(os.Args[2])
|
||||
var encBytes []byte
|
||||
if daemon.IsOpen() {
|
||||
encBytes = daemon.GetEncAndZeroizeDecBytes(decBytes)
|
||||
encBytes = daemon.GetEnc(decBytes, true)
|
||||
} else {
|
||||
password := front.InputSecret("Enter RCW password: ")
|
||||
if err := wrappers.RunSanityCheck(sanityFile, append([]byte{}, password...)); err != nil { // pass new slice to avoid zeroizing password)
|
||||
if err := wrappers.RunSanityCheck(sanityFile, password); err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
encBytes = wrappers.EncryptAndZeroizeDecBytesAndPassword(decBytes, password)
|
||||
encBytes = wrappers.Encrypt(decBytes, password, true, true)
|
||||
}
|
||||
os.WriteFile(outputFile, encBytes, 0600)
|
||||
return
|
||||
|
||||
@@ -4,7 +4,7 @@ go 1.25.7
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.6.2
|
||||
github.com/rwinkhart/go-boilerplate v0.2.3-0.20260211013046-29315c4b764f
|
||||
github.com/rwinkhart/go-boilerplate v0.2.3-0.20260211031648-26686374bd0c
|
||||
github.com/rwinkhart/peercred-mini v0.1.3
|
||||
golang.org/x/crypto v0.48.0
|
||||
golang.org/x/sys v0.41.0
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
github.com/rwinkhart/go-boilerplate v0.2.3-0.20260210031547-48e6abea8b2f h1:QloiAudLU8WAVs+loMU4ZiSd8ve0eiSkGtFhQUibhTw=
|
||||
github.com/rwinkhart/go-boilerplate v0.2.3-0.20260210031547-48e6abea8b2f/go.mod h1:ES13A2r9fnCVfyezwMBgY/RgA4pOIudOUXz3Jk/ikes=
|
||||
github.com/rwinkhart/go-boilerplate v0.2.3-0.20260211013046-29315c4b764f h1:ITbAql2EG50U0Z9JnPqupvdZ5zp0i44LnqZUsdz6DNc=
|
||||
github.com/rwinkhart/go-boilerplate v0.2.3-0.20260211013046-29315c4b764f/go.mod h1:ES13A2r9fnCVfyezwMBgY/RgA4pOIudOUXz3Jk/ikes=
|
||||
github.com/rwinkhart/go-boilerplate v0.2.3-0.20260211031648-26686374bd0c h1:N8rXbv1duCEF4qyiSx0hyb03A6AxWZ9w7mHAgM0kjUs=
|
||||
github.com/rwinkhart/go-boilerplate v0.2.3-0.20260211031648-26686374bd0c/go.mod h1:ES13A2r9fnCVfyezwMBgY/RgA4pOIudOUXz3Jk/ikes=
|
||||
github.com/rwinkhart/go-winio v0.1.1 h1:kAJKiqneR7cUR01Wn5/doAAV4kOGTEGPug4oinXc5N4=
|
||||
github.com/rwinkhart/go-winio v0.1.1/go.mod h1:ZWa7ssZJT30CCDGJ7fk/2SBTq9BIQrrVjrcss0UW2s0=
|
||||
github.com/rwinkhart/peercred-mini v0.1.3 h1:Pbyk/f4oXVtX38uuZAyxZZ8ZzRImL6WWgCr0pSktCKU=
|
||||
|
||||
@@ -24,7 +24,6 @@ func encryptAES(decBytes, key2, salt2 []byte) []byte {
|
||||
|
||||
// generate a random nonce
|
||||
nonce := getRandomBytes(nonceSizeAES)
|
||||
defer security.ZeroizeBytes(nonce)
|
||||
|
||||
// encrypt the data
|
||||
ciphertext := aesGCM.Seal(nil, nonce, decBytes, nil)
|
||||
|
||||
@@ -18,7 +18,6 @@ func encryptCha(decBytes, key2, salt2 []byte) []byte {
|
||||
|
||||
// generate a random nonce
|
||||
nonce := getRandomBytes(nonceSizeCha)
|
||||
defer security.ZeroizeBytes(nonce)
|
||||
|
||||
// encrypt the data
|
||||
ciphertext := stream.Seal(nil, nonce, decBytes, nil)
|
||||
|
||||
+14
-9
@@ -6,16 +6,18 @@ import (
|
||||
"github.com/rwinkhart/go-boilerplate/security"
|
||||
)
|
||||
|
||||
// DecryptAndZeroizePassword decrypts the provided byte slice using the provided password.
|
||||
func DecryptAndZeroizePassword(encBytes, password []byte) ([]byte, error) {
|
||||
// Decrypt decrypts the provided byte slice using the provided password.
|
||||
func Decrypt(encBytes, password []byte, zeroizePassword bool) ([]byte, error) {
|
||||
if len(encBytes) < saltSize1 {
|
||||
return nil, errors.New("high-level decrypt: encrypted data is too short (invalid Argon2 salt)")
|
||||
}
|
||||
salt1 := encBytes[:saltSize1]
|
||||
encBytes = encBytes[saltSize1:]
|
||||
key1 := derivePrimaryKey(password, salt1)
|
||||
security.ZeroizeBytes(password)
|
||||
security.ZeroizeBytes(salt1)
|
||||
defer security.ZeroizeBytes(key1)
|
||||
if zeroizePassword {
|
||||
security.ZeroizeBytes(password)
|
||||
}
|
||||
var err error
|
||||
encBytes, err = decryptCha(encBytes, key1)
|
||||
if err != nil {
|
||||
@@ -25,19 +27,22 @@ func DecryptAndZeroizePassword(encBytes, password []byte) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
security.ZeroizeBytes(key1)
|
||||
return encBytes, err
|
||||
}
|
||||
|
||||
// EncryptAndZeroizeDecBytesAndPassword encrypts the provided byte slice using the provided password.
|
||||
func EncryptAndZeroizeDecBytesAndPassword(decBytes, password []byte) []byte {
|
||||
defer security.ZeroizeBytes(decBytes)
|
||||
// Encrypt encrypts the provided byte slice using the provided password.
|
||||
func Encrypt(decBytes, password []byte, zeroizeDecBytes, zeroizePassword bool) []byte {
|
||||
if zeroizeDecBytes {
|
||||
defer security.ZeroizeBytes(decBytes)
|
||||
}
|
||||
salt1 := getRandomBytes(saltSize1)
|
||||
defer security.ZeroizeBytes(salt1)
|
||||
salt2AES := getRandomBytes(saltSize2)
|
||||
salt2Cha := getRandomBytes(saltSize2)
|
||||
key1 := derivePrimaryKey(password, salt1)
|
||||
security.ZeroizeBytes(password)
|
||||
if zeroizePassword {
|
||||
security.ZeroizeBytes(password)
|
||||
}
|
||||
key2AES := deriveSecondaryKey(key1, salt2AES, []byte(hkdfInfoAES))
|
||||
key2Cha := deriveSecondaryKey(key1, salt2Cha, []byte(hkdfInfoCha))
|
||||
security.ZeroizeBytes(key1)
|
||||
|
||||
@@ -4,16 +4,13 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/rwinkhart/go-boilerplate/security"
|
||||
)
|
||||
|
||||
// GenSanityCheckAndZeroizePassword creates an encrypted file containing known plaintext
|
||||
// GenSanityCheck creates an encrypted file containing known plaintext
|
||||
// to later be used for ensuring the user does not encrypt data with
|
||||
// an incorrect password.
|
||||
func GenSanityCheckAndZeroizePassword(path string, password []byte) error {
|
||||
err := os.WriteFile(path, EncryptAndZeroizeDecBytesAndPassword([]byte("thx4usin'rcw"), password), 0600)
|
||||
security.ZeroizeBytes(password)
|
||||
func GenSanityCheck(path string, password []byte, zeroizePassword bool) error {
|
||||
err := os.WriteFile(path, Encrypt([]byte("thx4usin'rcw"), password, false, zeroizePassword), 0600)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -25,9 +22,7 @@ func RunSanityCheck(path string, password []byte) error {
|
||||
if err != nil {
|
||||
return errors.New("Failed to read sanity check file (" + path + ")")
|
||||
}
|
||||
|
||||
// avoid zeroizing password, as this function expects the user to use the password after running it
|
||||
decBytes, err := DecryptAndZeroizePassword(encBytes, append([]byte{}, password...))
|
||||
decBytes, err := Decrypt(encBytes, password, false)
|
||||
if err == nil {
|
||||
if bytes.Equal(decBytes, []byte("thx4usin'rcw")) {
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user