Documentation
¶
Overview ¶
Package secure provides memory-safe handling of sensitive data.
This package wraps the memguard library to provide secure storage for secrets in memory. It ensures that sensitive data is:
- Encrypted at rest in memory (XSalsa20Poly1305)
- Protected from swapping via mlock
- Securely wiped when no longer needed
- Protected from buffer overflow via guard pages
Usage ¶
Create a secure buffer from sensitive bytes:
buf, err := secure.NewSecureBuffer([]byte("my-secret"))
if err != nil {
// Handle error - may indicate mlock unavailable
}
defer buf.Destroy() // Always destroy when done
// When you need to use the secret:
locked, err := buf.Open()
if err != nil {
// Handle error
}
defer locked.Destroy() // Destroy the unlocked buffer when done
// Use locked.Bytes() to access the plaintext
secretBytes := locked.Bytes()
Platform Behavior ¶
Memory locking behavior varies by platform:
- Linux: Requires RLIMIT_MEMLOCK to be set appropriately
- macOS: Works out of the box
- Windows: Uses VirtualLock
If mlock is unavailable or fails (e.g., due to RLIMIT_MEMLOCK), memguard handles this gracefully and continues with standard Go memory allocation. Note: memguard may log warnings internally; this package does not add additional logging.
Security Guarantees ¶
This package provides defense-in-depth against memory-based attacks:
- Core dumps will not contain plaintext secrets
- Secrets won't be swapped to disk
- Memory is overwritten with zeros on destruction
- Guard pages detect buffer overflows
It does NOT protect against:
- Attackers with root access to the running process
- Hardware-level attacks (cold boot, DMA)
- Spectre/Meltdown side-channel attacks
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrBufferDestroyed = errors.New("SecureBuffer has been destroyed")
ErrBufferDestroyed is returned when attempting to use a SecureBuffer after Destroy() was called.
Functions ¶
This section is empty.
Types ¶
type SecureBuffer ¶
type SecureBuffer struct {
// contains filtered or unexported fields
}
SecureBuffer provides memory-safe storage for sensitive data. It wraps memguard.Enclave to encrypt secrets at rest in memory and protect them from swapping via mlock.
Note: memguard.Enclave doesn't have a direct Destroy method. Instead, we track the enclave and use memguard.Purge() for cleanup at application exit, or simply let the enclave be garbage collected (the encrypted data is safe even without explicit destruction).
func NewSecureBuffer ¶
func NewSecureBuffer(data []byte) (*SecureBuffer, error)
NewSecureBuffer creates a protected buffer from secret bytes. The input data is immediately copied into a protected memory region and the original data remains unchanged (caller should zero it).
If mlock is unavailable (e.g., due to RLIMIT_MEMLOCK), memguard handles this gracefully and continues with standard memory allocation.
func NewSecureBufferFromString ¶
func NewSecureBufferFromString(s string) (*SecureBuffer, error)
NewSecureBufferFromString creates a SecureBuffer from a string. This is a convenience wrapper for NewSecureBuffer that handles the string-to-bytes conversion.
Note: Go strings are immutable, so the original string cannot be zeroed after this call. For maximum security, prefer working with []byte from the start and avoid string intermediates. However, wrapping in SecureBuffer still provides encrypted storage for subsequent operations.
func (*SecureBuffer) Destroy ¶
func (s *SecureBuffer) Destroy()
Destroy marks this SecureBuffer as destroyed and prevents further use. The underlying encrypted enclave data is safe even without explicit destruction since it's encrypted at rest. However, this method ensures the buffer cannot be accidentally reused.
This method is idempotent - calling it multiple times is safe. After Destroy(), Open() will return ErrBufferDestroyed.
For complete cleanup of all memguard data at application exit, call memguard.Purge() in a defer statement in main().
func (*SecureBuffer) Open ¶
func (s *SecureBuffer) Open() (*memguard.LockedBuffer, error)
Open decrypts and returns the protected data in a locked buffer. The caller MUST call Destroy() on the returned LockedBuffer when done to securely wipe the plaintext from memory.
Example:
locked, err := buf.Open()
if err != nil {
return err
}
defer locked.Destroy()
secret := locked.Bytes()