Documentation
¶
Overview ¶
Package e5t provides small AES-256-GCM encryption helpers built only on the Go standard library.
The package encrypts and decrypts byte slices with AES in Galois/Counter Mode (GCM). GCM is an authenticated encryption mode: successful decryption proves that the ciphertext, authentication tag, and nonce match the supplied key.
Encryption Format ¶
Encrypt returns raw bytes in this format:
nonce || ciphertext || authentication-tag
EncryptAsString returns the same bytes encoded as hexadecimal text. Decrypt expects the raw byte format, and DecryptFromText expects the hex-encoded format.
Keys ¶
AES-256 requires a 32-byte key. GenerateHashKey is a convenience helper that returns 32 bytes by hashing a key string plus an optional salt with SHA-256:
key := e5t.GenerateHashKey("application-secret", "config-v1")
For higher-risk secrets or user-entered passwords, pass a 32-byte key produced by a dedicated key management or password-based key derivation strategy.
Basic Usage ¶
key := e5t.GenerateHashKey("my-secret-password", "unique-salt")
encrypted, err := e5t.EncryptAsString([]byte("sensitive data"), key)
if err != nil {
log.Fatal(err)
}
decrypted, err := e5t.DecryptFromText(encrypted, key)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(decrypted))
Error Handling ¶
The package returns ErrInvalidKeySize when a key is not exactly 32 bytes and ErrCiphertextTooShort when encrypted input is shorter than the nonce prefix. Use errors.Is to branch on these sentinel errors.
Dependencies ¶
e5t has zero third-party dependencies. It uses crypto/aes, crypto/cipher, crypto/rand, crypto/sha256, encoding/hex, and other standard library packages.
Index ¶
- Variables
- func Decrypt(ciphertext []byte, key []byte) ([]byte, error)
- func DecryptFromText(hexCiphertext string, key []byte) ([]byte, error)
- func Encrypt(plaintext []byte, key []byte) ([]byte, error)
- func EncryptAsString(plaintext []byte, key []byte) (string, error)
- func GenerateHashKey(secret string, salt ...string) []byte
- func VerifyEncryption(original []byte, encrypted string, key []byte) (bool, error)
Examples ¶
- Decrypt (WrongKey)
- DecryptFromText
- Encrypt
- EncryptAsString
- EncryptAsString (ApiKey)
- EncryptAsString (Config)
- EncryptAsString (MultipleEncryptions)
- EncryptAsString (RoundTrip)
- EncryptAsString (UserSpecific)
- GenerateHashKey
- GenerateHashKey (WithUserID)
- VerifyEncryption
- VerifyEncryption (IntegrityCheck)
- VerifyEncryption (Mismatch)
Constants ¶
This section is empty.
Variables ¶
var ( // ErrInvalidKeySize is returned when a key is not exactly 32 bytes for AES-256. ErrInvalidKeySize = errors.New("key must be exactly 32 bytes for AES-256") // ErrCiphertextTooShort is returned when ciphertext is shorter than the nonce prefix. ErrCiphertextTooShort = errors.New("ciphertext shorter than nonce prefix") )
Sentinel errors for common error conditions.
Functions ¶
func Decrypt ¶
Decrypt decrypts raw bytes produced by Encrypt.
ciphertext must include the nonce prefix generated during encryption. key must be the same 32-byte key used for encryption.
Example (WrongKey) ¶
ExampleDecrypt_wrongKey demonstrates error handling with wrong key.
package main
import (
"fmt"
"log"
"github.com/slashdevops/e5t"
)
func main() {
// Encrypt with one key
key1 := e5t.GenerateHashKey("password1", "salt")
encrypted, err := e5t.EncryptAsString([]byte("secret"), key1)
if err != nil {
log.Fatal(err)
}
// Try to decrypt with wrong key
key2 := e5t.GenerateHashKey("password2", "salt")
_, err = e5t.DecryptFromText(encrypted, key2)
if err != nil {
fmt.Println("Decryption failed with wrong key")
}
}
Output: Decryption failed with wrong key
func DecryptFromText ¶
DecryptFromText decodes hexCiphertext and decrypts it with AES-256-GCM.
hexCiphertext must be a value returned by EncryptAsString. key must be the same 32-byte key used for encryption.
Example ¶
ExampleDecryptFromText demonstrates basic decryption from hex-encoded string.
package main
import (
"fmt"
"log"
"github.com/slashdevops/e5t"
)
func main() {
key := e5t.GenerateHashKey("my-secret-password", "random-salt")
// Encrypt
plaintext := []byte("Secret Message")
encrypted, err := e5t.EncryptAsString(plaintext, key)
if err != nil {
log.Fatal(err)
}
// Decrypt
decrypted, err := e5t.DecryptFromText(encrypted, key)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(decrypted))
}
Output: Secret Message
func Encrypt ¶
Encrypt encrypts plaintext with AES-256-GCM and returns raw encrypted bytes.
The returned bytes are formatted as nonce followed by encrypted data and the GCM authentication tag. key must be exactly 32 bytes.
Example ¶
ExampleEncrypt demonstrates basic encryption returning raw bytes.
package main
import (
"fmt"
"log"
"github.com/slashdevops/e5t"
)
func main() {
// Generate an encryption key
key := e5t.GenerateHashKey("my-secret-password", "random-salt")
// Encrypt some data
plaintext := []byte("Hello, World!")
encrypted, err := e5t.Encrypt(plaintext, key)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Encrypted data length: %d bytes\n", len(encrypted))
fmt.Println("Encrypted data is raw bytes")
}
Output: Encrypted data length: 41 bytes Encrypted data is raw bytes
func EncryptAsString ¶
EncryptAsString encrypts plaintext with AES-256-GCM and returns hex-encoded ciphertext.
The decoded ciphertext is formatted as nonce followed by encrypted data and the GCM authentication tag. key must be exactly 32 bytes.
Example ¶
ExampleEncryptAsString demonstrates basic encryption.
package main
import (
"fmt"
"log"
"github.com/slashdevops/e5t"
)
func main() {
// Generate an encryption key
key := e5t.GenerateHashKey("my-secret-password", "random-salt")
// Encrypt some data
plaintext := []byte("Hello, World!")
encrypted, err := e5t.EncryptAsString(plaintext, key)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Encrypted data length: %d characters\n", len(encrypted))
fmt.Println("Encrypted data is hex-encoded")
}
Output: Encrypted data length: 82 characters Encrypted data is hex-encoded
Example (ApiKey) ¶
ExampleEncryptAsString_apiKey demonstrates encrypting an API key.
package main
import (
"fmt"
"log"
"github.com/slashdevops/e5t"
)
func main() {
// Encrypt a sensitive API key
apiKey := []byte("sk_live_1234567890abcdef")
encryptionKey := e5t.GenerateHashKey("master-key", "api-keys-v1")
encrypted, err := e5t.EncryptAsString(apiKey, encryptionKey)
if err != nil {
log.Fatal(err)
}
fmt.Println("API key encrypted")
// Later, decrypt it
decrypted, err := e5t.DecryptFromText(encrypted, encryptionKey)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Decrypted API key: %s\n", string(decrypted))
}
Output: API key encrypted Decrypted API key: sk_live_1234567890abcdef
Example (Config) ¶
ExampleEncryptAsString_config demonstrates encrypting configuration data.
package main
import (
"fmt"
"log"
"github.com/slashdevops/e5t"
)
func main() {
// Simulate encrypting sensitive configuration
config := []byte(`{"db_password":"secret123","api_key":"abc-xyz"}`)
// Use application-specific key and salt
key := e5t.GenerateHashKey("app-master-key", "config-v1")
encrypted, err := e5t.EncryptAsString(config, key)
if err != nil {
log.Fatal(err)
}
fmt.Println("Configuration encrypted")
// Decrypt when needed
decrypted, err := e5t.DecryptFromText(encrypted, key)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Decrypted config length: %d bytes\n", len(decrypted))
}
Output: Configuration encrypted Decrypted config length: 47 bytes
Example (MultipleEncryptions) ¶
ExampleEncryptAsString_multipleEncryptions demonstrates that the same plaintext encrypts differently each time.
package main
import (
"fmt"
"log"
"github.com/slashdevops/e5t"
)
func main() {
key := e5t.GenerateHashKey("password", "salt")
message := []byte("same message")
// Encrypt the same message twice
encrypted1, err := e5t.EncryptAsString(message, key)
if err != nil {
log.Fatal(err)
}
encrypted2, err := e5t.EncryptAsString(message, key)
if err != nil {
log.Fatal(err)
}
// The encrypted values will be different
fmt.Printf("First encryption length: %d\n", len(encrypted1))
fmt.Printf("Second encryption length: %d\n", len(encrypted2))
fmt.Println("Each encryption produces unique ciphertext")
// But both decrypt to the same message
decrypted1, err := e5t.DecryptFromText(encrypted1, key)
if err != nil {
log.Fatal(err)
}
decrypted2, err := e5t.DecryptFromText(encrypted2, key)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Both decrypt correctly: %t\n", string(decrypted1) == string(decrypted2))
}
Output: First encryption length: 80 Second encryption length: 80 Each encryption produces unique ciphertext Both decrypt correctly: true
Example (RoundTrip) ¶
ExampleEncryptAsString_roundTrip demonstrates a complete encryption and decryption cycle.
package main
import (
"fmt"
"log"
"github.com/slashdevops/e5t"
)
func main() {
// Create a key
key := e5t.GenerateHashKey("secure-password", "app-salt-v1")
// Original message
message := []byte("This is a confidential message.")
// Encrypt
encrypted, err := e5t.EncryptAsString(message, key)
if err != nil {
log.Fatal(err)
}
fmt.Println("Message encrypted successfully")
// Decrypt
decrypted, err := e5t.DecryptFromText(encrypted, key)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Decrypted: %s\n", string(decrypted))
}
Output: Message encrypted successfully Decrypted: This is a confidential message.
Example (UserSpecific) ¶
ExampleEncryptAsString_userSpecific demonstrates per-user encryption.
package main
import (
"fmt"
"log"
"github.com/slashdevops/e5t"
)
func main() {
masterKey := "application-master-key"
// Encrypt data for user 1
user1ID := "user-001"
user1Key := e5t.GenerateHashKey(masterKey, user1ID)
user1Data := []byte("User 1 private data")
encrypted1, err := e5t.EncryptAsString(user1Data, user1Key)
if err != nil {
log.Fatal(err)
}
// Encrypt data for user 2
user2ID := "user-002"
user2Key := e5t.GenerateHashKey(masterKey, user2ID)
user2Data := []byte("User 2 private data")
encrypted2, err := e5t.EncryptAsString(user2Data, user2Key)
if err != nil {
log.Fatal(err)
}
fmt.Println("Each user has their own encrypted data")
// Each user can only decrypt their own data
decrypted1, err := e5t.DecryptFromText(encrypted1, user1Key)
if err != nil {
log.Fatal(err)
}
fmt.Printf("User 1 data: %s\n", string(decrypted1))
decrypted2, err := e5t.DecryptFromText(encrypted2, user2Key)
if err != nil {
log.Fatal(err)
}
fmt.Printf("User 2 data: %s\n", string(decrypted2))
}
Output: Each user has their own encrypted data User 1 data: User 1 private data User 2 data: User 2 private data
func GenerateHashKey ¶
GenerateHashKey creates a deterministic 32-byte key from secret and an optional salt.
It hashes secret plus the first salt value with SHA-256. This helper is useful for deriving stable AES-256 keys from application secrets or context strings. For user-entered passwords or high-risk secrets, prefer a dedicated key management or password-based key derivation strategy.
Example ¶
ExampleGenerateHashKey demonstrates basic key generation.
package main
import (
"fmt"
"github.com/slashdevops/e5t"
)
func main() {
// Generate a key without salt
key1 := e5t.GenerateHashKey("my-password")
fmt.Printf("Key length: %d bytes\n", len(key1))
// Generate a key with salt (recommended)
key2 := e5t.GenerateHashKey("my-password", "unique-salt")
fmt.Printf("Key with salt length: %d bytes\n", len(key2))
}
Output: Key length: 32 bytes Key with salt length: 32 bytes
Example (WithUserID) ¶
ExampleGenerateHashKey_withUserID demonstrates using user ID as salt.
package main
import (
"fmt"
"github.com/slashdevops/e5t"
)
func main() {
userID := "user-12345"
masterPassword := "app-master-secret"
// Generate a unique key for this user
userKey := e5t.GenerateHashKey(masterPassword, userID)
fmt.Printf("Generated user-specific key: %d bytes\n", len(userKey))
}
Output: Generated user-specific key: 32 bytes
func VerifyEncryption ¶
VerifyEncryption decrypts encrypted and compares the result with original.
encrypted must be a hex-encoded ciphertext returned by EncryptAsString. The function returns false with a nil error when decryption succeeds but the decrypted data does not match original.
Example ¶
ExampleVerifyEncryption demonstrates verifying encrypted data matches original data.
package main
import (
"fmt"
"log"
"github.com/slashdevops/e5t"
)
func main() {
key := e5t.GenerateHashKey("my-password", "my-salt")
original := []byte("Important data to verify")
encrypted, err := e5t.EncryptAsString(original, key)
if err != nil {
log.Fatal(err)
}
// Verify the encrypted data matches the original
match, err := e5t.VerifyEncryption(original, encrypted, key)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Data matches: %t\n", match)
}
Output: Data matches: true
Example (IntegrityCheck) ¶
ExampleVerifyEncryption_integrityCheck demonstrates using VerifyEncryption for integrity checking.
package main
import (
"fmt"
"log"
"github.com/slashdevops/e5t"
)
func main() {
key := e5t.GenerateHashKey("secure-key", "app-v1")
// Simulate storing data
userData := []byte("User profile data")
encryptedData, err := e5t.EncryptAsString(userData, key)
if err != nil {
log.Fatal(err)
}
// Store encryptedData in database/file...
// Later, retrieve and verify integrity
verified, err := e5t.VerifyEncryption(userData, encryptedData, key)
if err != nil {
fmt.Println("Verification failed:", err)
return
}
if verified {
fmt.Println("Data integrity verified successfully")
} else {
fmt.Println("Data integrity check failed - data was modified")
}
}
Output: Data integrity verified successfully
Example (Mismatch) ¶
ExampleVerifyEncryption_mismatch demonstrates verification with mismatched data.
package main
import (
"fmt"
"log"
"github.com/slashdevops/e5t"
)
func main() {
key := e5t.GenerateHashKey("password", "salt")
original := []byte("Original message")
different := []byte("Different message")
// Encrypt different data
encrypted, err := e5t.EncryptAsString(different, key)
if err != nil {
log.Fatal(err)
}
// Verify against original (should not match)
match, err := e5t.VerifyEncryption(original, encrypted, key)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Data matches: %t\n", match)
}
Output: Data matches: false
Types ¶
This section is empty.