Documentation
¶
Overview ¶
Package crypto provides NaCl SecretBox encryption for secure data storage.
This package implements symmetric encryption using NaCl SecretBox (XSalsa20 + Poly1305), which provides authenticated encryption. It is designed for encrypting sensitive data like OAuth tokens that need to be stored securely and decrypted later.
Security Properties ¶
- Confidentiality: XSalsa20 stream cipher with 256-bit key
- Integrity: Poly1305 MAC prevents tampering
- Nonce: 192-bit random nonce generated per encryption (no reuse risk)
Output Format ¶
Encrypted data is returned as Base64(nonce || ciphertext), where:
- nonce: 24 bytes (randomly generated)
- ciphertext: secretbox.Seal output (plaintext + 16 bytes overhead)
Usage Example ¶
// Create encryptor from Base64-encoded key
encryptor, err := crypto.NewEncryptorFromBase64(os.Getenv("ENCRYPTION_KEY"))
if err != nil {
log.Fatal(err)
}
// Encrypt sensitive data
encrypted, err := encryptor.Encrypt(oauthToken)
if err != nil {
return err
}
// Store encrypted in database...
// Later, decrypt
decrypted, err := encryptor.Decrypt(encrypted)
if err != nil {
return err
}
Key Generation ¶
Generate a 32-byte Base64-encoded key using openssl:
openssl rand -base64 32
Thread Safety ¶
All Encryptor implementations are safe for concurrent use. A single Encryptor instance can be shared across goroutines.
Key Management ¶
Keys should be managed securely:
- Store in environment variables or secret managers (AWS Secrets Manager, HashiCorp Vault)
- Never commit keys to source control
- Use different keys for different environments
- Plan for key rotation (re-encrypt existing data when rotating)
Key Rotation ¶
To rotate encryption keys in production without service disruption:
Generate new key: openssl rand -base64 32
Deploy collector with BOTH keys (old for decrypt, new as fallback)
Deploy web with new key (encrypts with new key)
Run batch re-encryption of existing database tokens:
oldEnc, _ := crypto.NewEncryptorFromBase64(oldKey) newEnc, _ := crypto.NewEncryptorFromBase64(newKey) decrypted, _ := oldEnc.Decrypt(ciphertext) newCiphertext, _ := newEnc.Encrypt(decrypted)
Remove old key from collector after re-encryption completes
Error Handling ¶
Wrap errors with service context for debugging distributed systems:
plaintext, err := enc.Decrypt(token)
if err != nil {
return fmt.Errorf("decrypt token [user=%s service=%s]: %w",
userID, "collector", err)
}
Memory Safety ¶
For long-lived server processes, call Close() when done with an Encryptor to zero sensitive key material from memory:
enc, _ := crypto.NewEncryptorFromBase64(key) defer enc.Close()
Index ¶
Constants ¶
const (
// KeyLength is the required length of encryption keys in bytes.
KeyLength = 32
)
Variables ¶
var ( // ErrInvalidKeyLength indicates the encryption key is not 32 bytes. ErrInvalidKeyLength = errors.New("crypto: invalid key length (expected 32 bytes)") // ErrDecryptionFailed indicates authentication or decryption failed. // This error is returned when the ciphertext cannot be authenticated, // typically due to wrong key or corrupted data. ErrDecryptionFailed = errors.New("crypto: decryption failed") // ErrInvalidCiphertext indicates the ciphertext is malformed. // This includes invalid base64 encoding or insufficient length. ErrInvalidCiphertext = errors.New("crypto: invalid ciphertext") // ErrEmptyPlaintext indicates an attempt to encrypt empty data. ErrEmptyPlaintext = errors.New("crypto: empty plaintext") )
Sentinel errors for encryption operations.
Functions ¶
This section is empty.
Types ¶
type Encryptor ¶
type Encryptor interface {
// Encrypt encrypts plaintext and returns Base64-encoded ciphertext.
// The output format is: Base64(nonce || encrypted_data)
// Returns ErrEmptyPlaintext if plaintext is empty.
Encrypt(plaintext string) (string, error)
// Decrypt decrypts Base64-encoded ciphertext and returns plaintext.
// Returns ErrInvalidCiphertext if input is malformed.
// Returns ErrDecryptionFailed if authentication fails.
Decrypt(ciphertext string) (string, error)
// Close zeros the encryption key from memory.
// After calling Close, the Encryptor should not be used.
// This is recommended for long-lived server processes to minimize
// the window during which keys are exposed in memory.
Close() error
}
Encryptor provides symmetric encryption for sensitive data. All implementations MUST be safe for concurrent use.
func NewEncryptor ¶
NewEncryptor creates a new Encryptor with the given 32-byte string key. Returns ErrInvalidKeyLength if the key is not exactly 32 bytes.
func NewEncryptorFromBase64 ¶
NewEncryptorFromBase64 creates a new Encryptor from a Base64-encoded key. The decoded key must be exactly 32 bytes. Returns ErrInvalidKeyLength if the key is invalid or not 32 bytes.