Rename all occurrences of "passphrase" to "password"

This commit is contained in:
2026-02-10 21:14:49 -05:00
parent 1745b85662
commit f49248930b
10 changed files with 53 additions and 54 deletions
+3 -3
View File
@@ -3,13 +3,13 @@ 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. It encrypts all data with both AES256-GCM and ChaCha20-Poly1305.
Passphrases are securely cached for three minutes and RPC authentication is used to Passwords are securely cached for three minutes and RPC authentication is used to
ensure that only the binary+user responsible for caching the passphrase can utilize it. ensure that only the binary+user responsible for caching the password can utilize it.
This feature is supported on Linux, FreeBSD, MacOS, and Windows. It is disabled if This feature is supported on Linux, FreeBSD, MacOS, and Windows. It is disabled if
built with `-tags=interactive`. built with `-tags=interactive`.
RCW also features a sanity check to ensure no data loss occurs due to a user entering the RCW also features a sanity check to ensure no data loss occurs due to a user entering the
incorrect passphrase during encryption. incorrect password during encryption.
Please note that RCW is a work-in-progress and breaking changes should be expected. Please note that RCW is a work-in-progress and breaking changes should be expected.
Future versions may not be capable of decrypting the output of the current version. Future versions may not be capable of decrypting the output of the current version.
+5 -5
View File
@@ -11,16 +11,16 @@ import (
var Timeout = 300 // seconds for RPC server timeout; configurable var Timeout = 300 // seconds for RPC server timeout; configurable
var daemonHash []byte var daemonHash []byte
var globalPassphrase []byte var globalPassword []byte
// RCWService provides an RPC method. // RCWService provides an RPC method.
type RCWService struct{} type RCWService struct{}
// DecryptRequest is the RPC method that decrypts the incoming data using // DecryptRequest is the RPC method that decrypts the incoming data using
// the global passphrase and returns the decrypted data // the global password and returns the decrypted data
func (h *RCWService) DecryptRequest(encBytes []byte, reply *[]byte) error { func (h *RCWService) DecryptRequest(encBytes []byte, reply *[]byte) error {
var err error var err error
*reply, err = wrappers.DecryptAndZeroizePassphrase(encBytes, append([]byte{}, globalPassphrase...)) // pass new slice to avoid zeroizing cached passphrase) *reply, err = wrappers.DecryptAndZeroizePassword(encBytes, append([]byte{}, globalPassword...)) // pass new slice to avoid zeroizing cached password)
if err != nil { if err != nil {
return err return err
} }
@@ -28,9 +28,9 @@ func (h *RCWService) DecryptRequest(encBytes []byte, reply *[]byte) error {
} }
// EncryptRequestAndZeroizeDecBytes is the RPC method that encrypts the incoming data using // EncryptRequestAndZeroizeDecBytes is the RPC method that encrypts the incoming data using
// the global passphrase and returns the encrypted data // the global password and returns the encrypted data
func (h *RCWService) EncryptRequestAndZeroizeDecBytes(decBytes []byte, reply *[]byte) error { func (h *RCWService) EncryptRequestAndZeroizeDecBytes(decBytes []byte, reply *[]byte) error {
*reply = wrappers.EncryptAndZeroizeDecBytesAndPassphrase(decBytes, append([]byte{}, globalPassphrase...)) // pass new slice to avoid zeroizing cached passphrase *reply = wrappers.EncryptAndZeroizeDecBytesAndPassword(decBytes, append([]byte{}, globalPassword...)) // pass new slice to avoid zeroizing cached password
return nil return nil
} }
+5 -5
View File
@@ -18,9 +18,9 @@ import (
// Start is the entry point for the RPC server responsible for // Start is the entry point for the RPC server responsible for
// returning decrypted data to authenticated clients. // returning decrypted data to authenticated clients.
func Start(passphrase []byte) { func Start(password []byte) {
// store passphrase to be referenced by DecryptRequest method // store password to be referenced by DecryptRequest method
globalPassphrase = passphrase globalPassword = password
// register RCWService with the RPC package // register RCWService with the RPC package
if err := rpc.Register(&RCWService{}); err != nil { if err := rpc.Register(&RCWService{}); err != nil {
@@ -69,9 +69,9 @@ func Start(passphrase []byte) {
// handleConn verifies the identity of the client. // handleConn verifies the identity of the client.
// It uses the file descriptor of the connection to get the PID of the client, // It uses the file descriptor of the connection to get the PID of the client,
// which is then used to get the path of the client's executable and calculate its hash. // which is then used to get the path of the client's executable and calculate its hash.
// The passphrase is only returned if the client's executable hash matches the daemon's hash // The password is only returned if the client's executable hash matches the daemon's hash
// and if the request is coming from the same user. // and if the request is coming from the same user.
// This ensures that only the binary the daemon is embedded in can retrieve the passphrase. // This ensures that only the binary the daemon is embedded in can retrieve the password.
func handleConn(conn net.Conn, sigChan chan os.Signal) { func handleConn(conn net.Conn, sigChan chan os.Signal) {
ucred := peercred.Get(conn) ucred := peercred.Get(conn)
+3 -3
View File
@@ -24,9 +24,9 @@ const (
// Start is the entry point for the RPC server responsible for // Start is the entry point for the RPC server responsible for
// returning decrypted data to authenticated clients. // returning decrypted data to authenticated clients.
func Start(passphrase []byte) { func Start(password []byte) {
// store passphrase to be referenced by DecryptRequest method // store password to be referenced by DecryptRequest method
globalPassphrase = passphrase globalPassword = password
// register RCWService with the RPC package // register RCWService with the RPC package
if err := rpc.Register(&RCWService{}); err != nil { if err := rpc.Register(&RCWService{}); err != nil {
+13 -16
View File
@@ -14,25 +14,25 @@ import (
// //
// Usage: // Usage:
// rcw init <passwd> : Generates the required sanity check file // rcw init <passwd> : Generates the required sanity check file
// rcw <passphrase> : Runs the rcw daemon to decrypt data for three minutes // rcw <password> : Runs the rcw daemon to decrypt data for three minutes
// rcw enc <text> : Encrypts the provided text and outputs the ciphertext to ex-cipher.rcw (attempts to use daemon, falls back to user input for passphrase) // rcw enc <text> : Encrypts the provided text and outputs the ciphertext to ex-cipher.rcw (attempts to use daemon, falls back to user input for password)
// rcw dec : Decrypts ex-cipher.rcw and outputs the plaintext to stdout (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 password)
// Implementation Notes: // Implementation Notes:
// There are two main ways to use the RCW library: // There are two main ways to use the RCW library:
// //
// 1. Daemon mode: // 1. Daemon mode:
// The daemon is started with a passphrase and runs in the background. // The daemon is started with a password and runs in the background.
// All encryption/decryption occurs in the daemon. // All encryption/decryption occurs in the daemon.
// Avoid using the wrapper.Encrypt/Decrypt functions directly. // Avoid using the wrapper.Encrypt/Decrypt functions directly.
// Instead, cache the passphrase with the daemon and use the daemon to encrypt/decrypt data. // Instead, cache the password with the daemon and use the daemon to encrypt/decrypt data.
// //
// 2. Standalone mode: // 2. Standalone mode:
// The wrapper.Encrypt/Decrypt functions are used directly. // The wrapper.Encrypt/Decrypt functions are used directly.
// The passphrase is provided directly to the functions. // The password is provided directly to the functions.
// //
// It is up to the client to perform the sanity check before encrypting data. // It is up to the client to perform the sanity check before encrypting data.
// This means that when using the daemon to cache the passphrase, the client should // This means that when using the daemon to cache the password, the client should
// perform the sanity check before activating the daemon. // perform the sanity check before activating the daemon.
// TODO Tests: // TODO Tests:
@@ -66,7 +66,7 @@ func main() {
if daemon.IsOpen() { if daemon.IsOpen() {
decBytes = daemon.GetDec(encBytes) decBytes = daemon.GetDec(encBytes)
} else { } else {
decBytes, err = wrappers.DecryptAndZeroizePassphrase(encBytes, front.InputHidden("Enter RCW passphrase:")) decBytes, err = wrappers.DecryptAndZeroizePassword(encBytes, front.InputSecret("Enter RCW password:"))
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
@@ -81,8 +81,7 @@ func main() {
fmt.Println("Daemon already running") fmt.Println("Daemon already running")
return return
} }
err := wrappers.RunSanityCheck(sanityFile, []byte(os.Args[1])) if err := wrappers.RunSanityCheck(sanityFile, []byte(os.Args[1])); err != nil {
if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
@@ -91,8 +90,7 @@ func main() {
if os.Args[1] == "init" { if os.Args[1] == "init" {
// create sanity check file // create sanity check file
// rcw init <passwd> // rcw init <passwd>
err := wrappers.GenSanityCheckAndZeroizePassphrase(sanityFile, []byte(os.Args[2])) if err := wrappers.GenSanityCheckAndZeroizePassword(sanityFile, []byte(os.Args[2])); err != nil {
if err != nil {
fmt.Println(err) fmt.Println(err)
} }
return return
@@ -104,13 +102,12 @@ func main() {
if daemon.IsOpen() { if daemon.IsOpen() {
encBytes = daemon.GetEncAndZeroizeDecBytes(decBytes) encBytes = daemon.GetEncAndZeroizeDecBytes(decBytes)
} else { } else {
passphrase := front.InputHidden("Enter RCW passphrase: ") password := front.InputSecret("Enter RCW password: ")
err := wrappers.RunSanityCheck(sanityFile, append([]byte{}, passphrase...)) // pass new slice to avoid zeroizing passphrase) if err := wrappers.RunSanityCheck(sanityFile, append([]byte{}, password...)); err != nil { // pass new slice to avoid zeroizing password)
if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
encBytes = wrappers.EncryptAndZeroizeDecBytesAndPassphrase(decBytes, passphrase) encBytes = wrappers.EncryptAndZeroizeDecBytesAndPassword(decBytes, password)
} }
os.WriteFile(outputFile, encBytes, 0600) os.WriteFile(outputFile, encBytes, 0600)
return return
+1 -1
View File
@@ -4,7 +4,7 @@ go 1.25.7
require ( require (
github.com/Microsoft/go-winio v0.6.2 github.com/Microsoft/go-winio v0.6.2
github.com/rwinkhart/go-boilerplate v0.2.3-0.20260210031547-48e6abea8b2f github.com/rwinkhart/go-boilerplate v0.2.3-0.20260211013046-29315c4b764f
github.com/rwinkhart/peercred-mini v0.1.3 github.com/rwinkhart/peercred-mini v0.1.3
golang.org/x/crypto v0.48.0 golang.org/x/crypto v0.48.0
golang.org/x/sys v0.41.0 golang.org/x/sys v0.41.0
+2
View File
@@ -1,5 +1,7 @@
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 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.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-winio v0.1.1 h1:kAJKiqneR7cUR01Wn5/doAAV4kOGTEGPug4oinXc5N4= 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/go-winio v0.1.1/go.mod h1:ZWa7ssZJT30CCDGJ7fk/2SBTq9BIQrrVjrcss0UW2s0=
github.com/rwinkhart/peercred-mini v0.1.3 h1:Pbyk/f4oXVtX38uuZAyxZZ8ZzRImL6WWgCr0pSktCKU= github.com/rwinkhart/peercred-mini v0.1.3 h1:Pbyk/f4oXVtX38uuZAyxZZ8ZzRImL6WWgCr0pSktCKU=
+8 -8
View File
@@ -6,15 +6,15 @@ import (
"github.com/rwinkhart/go-boilerplate/security" "github.com/rwinkhart/go-boilerplate/security"
) )
// DecryptAndZeroizePassphrase decrypts the provided byte slice using the provided passphrase. // DecryptAndZeroizePassword decrypts the provided byte slice using the provided password.
func DecryptAndZeroizePassphrase(encBytes, passphrase []byte) ([]byte, error) { func DecryptAndZeroizePassword(encBytes, password []byte) ([]byte, error) {
if len(encBytes) < saltSize1 { if len(encBytes) < saltSize1 {
return nil, errors.New("high-level decrypt: encrypted data is too short (invalid Argon2 salt)") return nil, errors.New("high-level decrypt: encrypted data is too short (invalid Argon2 salt)")
} }
salt1 := encBytes[:saltSize1] salt1 := encBytes[:saltSize1]
encBytes = encBytes[saltSize1:] encBytes = encBytes[saltSize1:]
key1 := derivePrimaryKey(passphrase, salt1) key1 := derivePrimaryKey(password, salt1)
security.ZeroizeBytes(passphrase) security.ZeroizeBytes(password)
security.ZeroizeBytes(salt1) security.ZeroizeBytes(salt1)
var err error var err error
encBytes, err = decryptCha(encBytes, key1) encBytes, err = decryptCha(encBytes, key1)
@@ -29,15 +29,15 @@ func DecryptAndZeroizePassphrase(encBytes, passphrase []byte) ([]byte, error) {
return encBytes, err return encBytes, err
} }
// EncryptAndZeroizeDecBytesAndPassphrase encrypts the provided byte slice using the provided passphrase. // EncryptAndZeroizeDecBytesAndPassword encrypts the provided byte slice using the provided password.
func EncryptAndZeroizeDecBytesAndPassphrase(decBytes, passphrase []byte) []byte { func EncryptAndZeroizeDecBytesAndPassword(decBytes, password []byte) []byte {
defer security.ZeroizeBytes(decBytes) defer security.ZeroizeBytes(decBytes)
salt1 := getRandomBytes(saltSize1) salt1 := getRandomBytes(saltSize1)
defer security.ZeroizeBytes(salt1) defer security.ZeroizeBytes(salt1)
salt2AES := getRandomBytes(saltSize2) salt2AES := getRandomBytes(saltSize2)
salt2Cha := getRandomBytes(saltSize2) salt2Cha := getRandomBytes(saltSize2)
key1 := derivePrimaryKey(passphrase, salt1) key1 := derivePrimaryKey(password, salt1)
security.ZeroizeBytes(passphrase) security.ZeroizeBytes(password)
key2AES := deriveSecondaryKey(key1, salt2AES, []byte(hkdfInfoAES)) key2AES := deriveSecondaryKey(key1, salt2AES, []byte(hkdfInfoAES))
key2Cha := deriveSecondaryKey(key1, salt2Cha, []byte(hkdfInfoCha)) key2Cha := deriveSecondaryKey(key1, salt2Cha, []byte(hkdfInfoCha))
security.ZeroizeBytes(key1) security.ZeroizeBytes(key1)
+4 -4
View File
@@ -21,14 +21,14 @@ const (
saltSize2 = 32 // 256 bits, recommended salt size for HKDF saltSize2 = 32 // 256 bits, recommended salt size for HKDF
) )
// derivePrimaryKey derives an encryption key from a passphrase using Argon2. // derivePrimaryKey derives an encryption key from a password using Argon2.
// The resulting key is not meant to be used directly for encryption, but rather as a key to derive other keys. // The resulting key is not meant to be used directly for encryption, but rather as a key to derive other keys.
func derivePrimaryKey(passphrase, salt []byte) []byte { func derivePrimaryKey(password, salt []byte) []byte {
return argon2.IDKey(passphrase, salt, argonTime, argonMemory, argonThreads, keyLen) return argon2.IDKey(password, salt, argonTime, argonMemory, argonThreads, keyLen)
} }
// deriveSecondaryKey derives a secondary key from the primary key using HKDF. // deriveSecondaryKey derives a secondary key from the primary key using HKDF.
// It is meant to be an efficient way to derive multiple keys from a single passphrase. // It is meant to be an efficient way to derive multiple keys from a single password.
func deriveSecondaryKey(primaryKey, salt, info []byte) []byte { func deriveSecondaryKey(primaryKey, salt, info []byte) []byte {
h := hkdf.New(sha256.New, primaryKey, salt, info) h := hkdf.New(sha256.New, primaryKey, salt, info)
derivedKey := make([]byte, keyLen) derivedKey := make([]byte, keyLen)
+9 -9
View File
@@ -8,28 +8,28 @@ import (
"github.com/rwinkhart/go-boilerplate/security" "github.com/rwinkhart/go-boilerplate/security"
) )
// GenSanityCheckAndZeroizePassphrase creates an encrypted file containing known plaintext // GenSanityCheckAndZeroizePassword creates an encrypted file containing known plaintext
// to later be used for ensuring the user does not encrypt data with // to later be used for ensuring the user does not encrypt data with
// an incorrect passphrase. // an incorrect password.
func GenSanityCheckAndZeroizePassphrase(path string, passphrase []byte) error { func GenSanityCheckAndZeroizePassword(path string, password []byte) error {
err := os.WriteFile(path, EncryptAndZeroizeDecBytesAndPassphrase([]byte("thx4usin'rcw"), passphrase), 0600) err := os.WriteFile(path, EncryptAndZeroizeDecBytesAndPassword([]byte("thx4usin'rcw"), password), 0600)
security.ZeroizeBytes(passphrase) security.ZeroizeBytes(password)
return err return err
} }
// RunSanityCheck should be run before any encryption operation // RunSanityCheck should be run before any encryption operation
// to ensure the user does not encrypt data with an incorrect passphrase. // to ensure the user does not encrypt data with an incorrect password.
// Failure to perform this check could result in data loss. // Failure to perform this check could result in data loss.
func RunSanityCheck(path string, passphrase []byte) error { func RunSanityCheck(path string, password []byte) error {
encBytes, err := os.ReadFile(path) encBytes, err := os.ReadFile(path)
if err != nil { if err != nil {
return errors.New("Failed to read sanity check file (" + path + ")") return errors.New("Failed to read sanity check file (" + path + ")")
} }
decBytes, err := DecryptAndZeroizePassphrase(encBytes, passphrase) decBytes, err := DecryptAndZeroizePassword(encBytes, password)
if err == nil { if err == nil {
if bytes.Equal(decBytes, []byte("thx4usin'rcw")) { if bytes.Equal(decBytes, []byte("thx4usin'rcw")) {
return nil return nil
} }
} }
return errors.New("sanity check failed (likely due to inconsistent passphrase)") return errors.New("sanity check failed (likely due to inconsistent password)")
} }