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.
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 utilize it.
Passwords are securely cached for three minutes and RPC authentication is used to
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
built with `-tags=interactive`.
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.
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 daemonHash []byte
var globalPassphrase []byte
var globalPassword []byte
// RCWService provides an RPC method.
type RCWService struct{}
// 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 {
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 {
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
// 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 {
*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
}
+5 -5
View File
@@ -18,9 +18,9 @@ import (
// 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
func Start(password []byte) {
// store password to be referenced by DecryptRequest method
globalPassword = password
// register RCWService with the RPC package
if err := rpc.Register(&RCWService{}); err != nil {
@@ -69,9 +69,9 @@ func Start(passphrase []byte) {
// handleConn verifies the identity 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.
// 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.
// 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) {
ucred := peercred.Get(conn)
+3 -3
View File
@@ -24,9 +24,9 @@ const (
// 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
func Start(password []byte) {
// store password to be referenced by DecryptRequest method
globalPassword = password
// register RCWService with the RPC package
if err := rpc.Register(&RCWService{}); err != nil {
+13 -16
View File
@@ -14,25 +14,25 @@ import (
//
// Usage:
// rcw init <passwd> : Generates the required sanity check file
// rcw <passphrase> : 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 dec : Decrypts ex-cipher.rcw and outputs the plaintext to stdout (attempts to use daemon, falls back to user input for passphrase)
// 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 password)
// 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:
// There are two main ways to use the RCW library:
//
// 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.
// 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:
// 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.
// 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.
// TODO Tests:
@@ -66,7 +66,7 @@ func main() {
if daemon.IsOpen() {
decBytes = daemon.GetDec(encBytes)
} else {
decBytes, err = wrappers.DecryptAndZeroizePassphrase(encBytes, front.InputHidden("Enter RCW passphrase:"))
decBytes, err = wrappers.DecryptAndZeroizePassword(encBytes, front.InputSecret("Enter RCW password:"))
if err != nil {
fmt.Println(err)
return
@@ -81,8 +81,7 @@ func main() {
fmt.Println("Daemon already running")
return
}
err := wrappers.RunSanityCheck(sanityFile, []byte(os.Args[1]))
if err != nil {
if err := wrappers.RunSanityCheck(sanityFile, []byte(os.Args[1])); err != nil {
fmt.Println(err)
return
}
@@ -91,8 +90,7 @@ func main() {
if os.Args[1] == "init" {
// create sanity check file
// rcw init <passwd>
err := wrappers.GenSanityCheckAndZeroizePassphrase(sanityFile, []byte(os.Args[2]))
if err != nil {
if err := wrappers.GenSanityCheckAndZeroizePassword(sanityFile, []byte(os.Args[2])); err != nil {
fmt.Println(err)
}
return
@@ -104,13 +102,12 @@ func main() {
if daemon.IsOpen() {
encBytes = daemon.GetEncAndZeroizeDecBytes(decBytes)
} else {
passphrase := front.InputHidden("Enter RCW passphrase: ")
err := wrappers.RunSanityCheck(sanityFile, append([]byte{}, passphrase...)) // pass new slice to avoid zeroizing passphrase)
if err != nil {
password := front.InputSecret("Enter RCW password: ")
if err := wrappers.RunSanityCheck(sanityFile, append([]byte{}, password...)); err != nil { // pass new slice to avoid zeroizing password)
fmt.Println(err)
return
}
encBytes = wrappers.EncryptAndZeroizeDecBytesAndPassphrase(decBytes, passphrase)
encBytes = wrappers.EncryptAndZeroizeDecBytesAndPassword(decBytes, password)
}
os.WriteFile(outputFile, encBytes, 0600)
return
+1 -1
View File
@@ -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.20260210031547-48e6abea8b2f
github.com/rwinkhart/go-boilerplate v0.2.3-0.20260211013046-29315c4b764f
github.com/rwinkhart/peercred-mini v0.1.3
golang.org/x/crypto v0.48.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/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/go.mod h1:ZWa7ssZJT30CCDGJ7fk/2SBTq9BIQrrVjrcss0UW2s0=
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"
)
// DecryptAndZeroizePassphrase decrypts the provided byte slice using the provided passphrase.
func DecryptAndZeroizePassphrase(encBytes, passphrase []byte) ([]byte, error) {
// DecryptAndZeroizePassword decrypts the provided byte slice using the provided password.
func DecryptAndZeroizePassword(encBytes, password []byte) ([]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(passphrase, salt1)
security.ZeroizeBytes(passphrase)
key1 := derivePrimaryKey(password, salt1)
security.ZeroizeBytes(password)
security.ZeroizeBytes(salt1)
var err error
encBytes, err = decryptCha(encBytes, key1)
@@ -29,15 +29,15 @@ func DecryptAndZeroizePassphrase(encBytes, passphrase []byte) ([]byte, error) {
return encBytes, err
}
// EncryptAndZeroizeDecBytesAndPassphrase encrypts the provided byte slice using the provided passphrase.
func EncryptAndZeroizeDecBytesAndPassphrase(decBytes, passphrase []byte) []byte {
// EncryptAndZeroizeDecBytesAndPassword encrypts the provided byte slice using the provided password.
func EncryptAndZeroizeDecBytesAndPassword(decBytes, password []byte) []byte {
defer security.ZeroizeBytes(decBytes)
salt1 := getRandomBytes(saltSize1)
defer security.ZeroizeBytes(salt1)
salt2AES := getRandomBytes(saltSize2)
salt2Cha := getRandomBytes(saltSize2)
key1 := derivePrimaryKey(passphrase, salt1)
security.ZeroizeBytes(passphrase)
key1 := derivePrimaryKey(password, salt1)
security.ZeroizeBytes(password)
key2AES := deriveSecondaryKey(key1, salt2AES, []byte(hkdfInfoAES))
key2Cha := deriveSecondaryKey(key1, salt2Cha, []byte(hkdfInfoCha))
security.ZeroizeBytes(key1)
+4 -4
View File
@@ -21,14 +21,14 @@ const (
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.
func derivePrimaryKey(passphrase, salt []byte) []byte {
return argon2.IDKey(passphrase, salt, argonTime, argonMemory, argonThreads, keyLen)
func derivePrimaryKey(password, salt []byte) []byte {
return argon2.IDKey(password, salt, argonTime, argonMemory, argonThreads, keyLen)
}
// 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 {
h := hkdf.New(sha256.New, primaryKey, salt, info)
derivedKey := make([]byte, keyLen)
+9 -9
View File
@@ -8,28 +8,28 @@ import (
"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
// an incorrect passphrase.
func GenSanityCheckAndZeroizePassphrase(path string, passphrase []byte) error {
err := os.WriteFile(path, EncryptAndZeroizeDecBytesAndPassphrase([]byte("thx4usin'rcw"), passphrase), 0600)
security.ZeroizeBytes(passphrase)
// an incorrect password.
func GenSanityCheckAndZeroizePassword(path string, password []byte) error {
err := os.WriteFile(path, EncryptAndZeroizeDecBytesAndPassword([]byte("thx4usin'rcw"), password), 0600)
security.ZeroizeBytes(password)
return err
}
// 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.
func RunSanityCheck(path string, passphrase []byte) error {
func RunSanityCheck(path string, password []byte) error {
encBytes, err := os.ReadFile(path)
if err != nil {
return errors.New("Failed to read sanity check file (" + path + ")")
}
decBytes, err := DecryptAndZeroizePassphrase(encBytes, passphrase)
decBytes, err := DecryptAndZeroizePassword(encBytes, password)
if err == nil {
if bytes.Equal(decBytes, []byte("thx4usin'rcw")) {
return nil
}
}
return errors.New("sanity check failed (likely due to inconsistent passphrase)")
return errors.New("sanity check failed (likely due to inconsistent password)")
}