mirror of
https://github.com/rwinkhart/rcw.git
synced 2026-08-28 04:46:42 -04:00
Rename all occurrences of "passphrase" to "password"
This commit is contained in:
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user