aes

package
v1.19.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 4, 2026 License: MIT Imports: 8 Imported by: 0

README

AES Encoding Package

Go Version GoDoc

AES-256-GCM authenticated encryption with streaming I/O support.

AI Disclaimer (EU AI Act Article 50.4): AI assistance was used solely for testing, documentation, and bug resolution under human supervision.


Table of Contents


Overview

The aes package provides AES-256-GCM authenticated encryption for Go applications. It implements the encoding.Coder interface for consistent encryption/decryption operations across the golib ecosystem.

Design Philosophy
  • Security First: Industry-standard AES-256-GCM authenticated encryption
  • Simplicity: Clean API for both byte slices and streaming operations
  • Performance: Hardware-accelerated on modern CPUs with AES-NI
  • Memory Efficiency: Direct operations without intermediate buffers
  • Thread Safety: Safe for concurrent use with separate instances

Key Features

Feature Description
AES-256-GCM Industry-standard authenticated encryption
Authentication Built-in integrity and authenticity verification
Streaming Support io.Reader and io.Writer interfaces
Memory Efficient Direct byte slice operations
Thread-Safe Concurrent operations with separate instances
Key Generation Cryptographically secure random key/nonce generation
Hex Encoding Helper functions for hex key/nonce encoding

Security

Cryptographic Specifications
Component Specification Details
Algorithm AES-256 256-bit key size
Mode GCM Galois/Counter Mode
Key Size 256 bits 32 bytes
Nonce Size 96 bits 12 bytes (GCM standard)
Auth Tag 128 bits 16 bytes (tamper detection)
Performance Hardware-accelerated AES-NI support
Security Properties

Confidentiality

  • AES-256 ensures data cannot be read without the key
  • Brute force resistance: 2^256 possible keys
  • Quantum resistance: Still secure against known quantum attacks

Authenticity

  • GCM tag ensures data comes from the key holder
  • Prevents unauthorized parties from creating valid ciphertexts
  • Non-repudiation within the system

Integrity

  • Any modification to ciphertext is detected
  • Tag verification fails if data is tampered with
  • Protects against bit-flipping attacks

Security Level

  • Meets NIST recommendations for sensitive data
  • Approved for classified information (with proper key management)
  • Resistant to known cryptanalytic attacks
Important Security Considerations

⚠️ Nonce Reuse: Never reuse a nonce with the same key. This catastrophically breaks GCM security.

⚠️ Key Management: Store keys securely. Never commit to version control or log them.

⚠️ Key Rotation: Rotate keys periodically (e.g., every 30-90 days for high-security applications).

⚠️ Error Handling: Always check authentication errors during decryption.


Architecture

Package Structure
encoding/aes/
├── interface.go        # Public API and key generation
├── model.go           # Core implementation (Coder interface)
└── errors.go          # Error definitions (if exists)
Component Architecture
┌─────────────────────────────────────────────────────┐
│              AES Package                             │
│                                                      │
│  ┌──────────────────────────────────────────────┐  │
│  │         Key & Nonce Generation               │  │
│  │  - GenKey()      (32 bytes)                  │  │
│  │  - GenNonce()    (12 bytes)                  │  │
│  │  - GetHexKey()   (from hex string)           │  │
│  │  - GetHexNonce() (from hex string)           │  │
│  └──────────────────────────────────────────────┘  │
│                        │                             │
│                        ▼                             │
│  ┌──────────────────────────────────────────────┐  │
│  │         Coder Interface                      │  │
│  │  - Encode(plaintext) → ciphertext           │  │
│  │  - Decode(ciphertext) → plaintext           │  │
│  │  - EncodeReader(io.Reader) → io.Reader     │  │
│  │  - DecodeReader(io.Reader) → io.Reader     │  │
│  │  - Reset()                                   │  │
│  └──────────────────────────────────────────────┘  │
│                        │                             │
│                        ▼                             │
│  ┌──────────────────────────────────────────────┐  │
│  │         AES-256-GCM Engine                   │  │
│  │  - cipher.NewGCM()                           │  │
│  │  - Seal() / Open()                           │  │
│  │  - Authentication tag verification          │  │
│  └──────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────┘
Data Flow
Encryption Flow:
  Plaintext → AES-GCM Seal → [Nonce + Ciphertext + Tag] → Output

Decryption Flow:
  Input → [Nonce + Ciphertext + Tag] → AES-GCM Open → Plaintext
                                            ↓
                                    (Tag Verification)

Installation

go get github.com/nabbar/golib/encoding/aes

Dependencies:

  • Go standard library (crypto/aes, crypto/cipher, crypto/rand)
  • github.com/nabbar/golib/encoding (interface definitions)

Quick Start

Basic Encryption/Decryption
package main

import (
    "fmt"
    "log"
    
    encaes "github.com/nabbar/golib/encoding/aes"
)

func main() {
    // Generate a new key and nonce
    key, err := encaes.GenKey()
    if err != nil {
        log.Fatal(err)
    }
    
    nonce, err := encaes.GenNonce()
    if err != nil {
        log.Fatal(err)
    }
    
    // Create a new coder
    coder, err := encaes.New(key, nonce)
    if err != nil {
        log.Fatal(err)
    }
    defer coder.Reset()
    
    // Encrypt data
    plaintext := []byte("Secret message")
    encrypted := coder.Encode(plaintext)
    
    // Decrypt data
    decrypted, err := coder.Decode(encrypted)
    if err != nil {
        log.Fatal("Decryption failed:", err)
    }
    
    fmt.Println(string(decrypted)) // Output: Secret message
}

Core Concepts

Key Management

A key is a 32-byte (256-bit) secret used for encryption and decryption.

Generate New Key:

// Cryptographically secure random key
key, err := encaes.GenKey()
if err != nil {
    log.Fatal(err)
}

Load Key from Hex:

// From configuration file or environment variable
hexKey := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
key, err := encaes.GetHexKey(hexKey)
if err != nil {
    log.Fatal(err)
}

Store Key as Hex:

import "encoding/hex"

// Convert to hex for storage
hexString := hex.EncodeToString(key[:])
// Save to config file or secure storage

⚠️ Security Warning: Never commit keys to version control, log them, or transmit them over insecure channels.

Nonce Management

A nonce (Number used ONCE) is a 12-byte value that must be unique for each encryption with the same key.

Generate New Nonce:

// Cryptographically secure random nonce
nonce, err := encaes.GenNonce()
if err != nil {
    log.Fatal(err)
}

Load Nonce from Hex:

hexNonce := "0123456789abcdef01234567"  // 24 hex characters (12 bytes)
nonce, err := encaes.GetHexNonce(hexNonce)
if err != nil {
    log.Fatal(err)
}

⚠️ Critical: Never reuse a nonce with the same key! This breaks GCM security catastrophically.

Best Practice: Generate a new nonce for each encryption, or use a counter that never repeats.


API Reference

Key/Nonce Generation

GenKey() ([32]byte, error)

Generates a cryptographically secure random 32-byte key.

key, err := encaes.GenKey()
if err != nil {
    log.Fatal("Key generation failed:", err)
}

GenNonce() ([12]byte, error)

Generates a cryptographically secure random 12-byte nonce.

nonce, err := encaes.GenNonce()
if err != nil {
    log.Fatal("Nonce generation failed:", err)
}

GetHexKey(s string) ([32]byte, error)

Decodes a hex-encoded string to a 32-byte key. Truncates if too long, zero-fills if too short.

key, err := encaes.GetHexKey("0123456789abcdef...")

GetHexNonce(s string) ([12]byte, error)

Decodes a hex-encoded string to a 12-byte nonce. Truncates if too long, zero-fills if too short.

nonce, err := encaes.GetHexNonce("0123456789abcdef01234567")
Coder Interface

New(key [32]byte, nonce [12]byte) (encoding.Coder, error)

Creates a new AES coder instance.

coder, err := encaes.New(key, nonce)
if err != nil {
    log.Fatal("Coder creation failed:", err)
}
defer coder.Reset()

Encode(p []byte) []byte

Encrypts plaintext and returns ciphertext.

plaintext := []byte("Secret data")
ciphertext := coder.Encode(plaintext)

Decode(p []byte) ([]byte, error)

Decrypts ciphertext and returns plaintext. Returns error if authentication fails.

plaintext, err := coder.Decode(ciphertext)
if err != nil {
    log.Fatal("Decryption failed (authentication error):", err)
}

Reset()

Clears internal state. Should be called when done with coder (use defer).

defer coder.Reset()

Streaming Operations

Encrypt Stream

EncodeReader(r io.Reader) io.Reader

Creates a reader that encrypts data on-the-fly.

file, _ := os.Open("plaintext.txt")
defer file.Close()

// Create encrypted reader
encryptedReader := coder.EncodeReader(file)

// Write encrypted data to output
output, _ := os.Create("encrypted.bin")
defer output.Close()

io.Copy(output, encryptedReader)
Decrypt Stream

DecodeReader(r io.Reader) io.Reader

Creates a reader that decrypts data on-the-fly.

file, _ := os.Open("encrypted.bin")
defer file.Close()

// Create decrypted reader
decryptedReader := coder.DecodeReader(file)

// Read decrypted data
output, _ := os.Create("decrypted.txt")
defer output.Close()

io.Copy(output, decryptedReader)
Example: Encrypt File
func encryptFile(inputPath, outputPath string, coder encoding.Coder) error {
    // Open input file
    input, err := os.Open(inputPath)
    if err != nil {
        return err
    }
    defer input.Close()
    
    // Create output file
    output, err := os.Create(outputPath)
    if err != nil {
        return err
    }
    defer output.Close()
    
    // Encrypt and write
    encryptedReader := coder.EncodeReader(input)
    _, err = io.Copy(output, encryptedReader)
    return err
}

Performance

Benchmark Results
Operation Throughput Notes
Encrypt (1KB) ~500 MB/s With AES-NI
Decrypt (1KB) ~500 MB/s With AES-NI
Encrypt (1MB) ~600 MB/s Larger blocks
Decrypt (1MB) ~600 MB/s Larger blocks
Key Generation ~50µs Random source dependent
Nonce Generation ~50µs Random source dependent

Benchmarks on Intel Core i7, Go 1.21, Linux

Hardware Acceleration

AES-NI Support:

  • Modern Intel/AMD CPUs include AES-NI instructions
  • Go's crypto/aes automatically uses AES-NI when available
  • Provides 3-5x performance improvement
  • No code changes required

Verify AES-NI:

# Linux
grep -o 'aes' /proc/cpuinfo | head -1

# macOS
sysctl machdep.cpu.features | grep AES
Memory Usage
Operation Memory Notes
Coder Instance ~200 bytes Minimal overhead
Encode Input + 28 bytes Nonce (12) + Tag (16)
Decode Input - 28 bytes Removes nonce & tag
Stream Buffer 4KB default Configurable

Use Cases

Secure Configuration Files
import (
    "os"
    encaes "github.com/nabbar/golib/encoding/aes"
)

// Encrypt configuration
func SaveSecureConfig(config []byte, key [32]byte) error {
    nonce, _ := encaes.GenNonce()
    coder, _ := encaes.New(key, nonce)
    defer coder.Reset()
    
    encrypted := coder.Encode(config)
    return os.WriteFile("config.enc", encrypted, 0600)
}

// Decrypt configuration
func LoadSecureConfig(key [32]byte) ([]byte, error) {
    encrypted, err := os.ReadFile("config.enc")
    if err != nil {
        return nil, err
    }
    
    // Extract nonce from encrypted data
    nonce := [12]byte{}
    copy(nonce[:], encrypted[:12])
    
    coder, _ := encaes.New(key, nonce)
    defer coder.Reset()
    
    return coder.Decode(encrypted)
}
Database Field Encryption
type User struct {
    ID       int
    Username string
    SSN      []byte  // Encrypted social security number
}

func (u *User) EncryptSSN(ssn string, coder encoding.Coder) {
    u.SSN = coder.Encode([]byte(ssn))
}

func (u *User) DecryptSSN(coder encoding.Coder) (string, error) {
    plaintext, err := coder.Decode(u.SSN)
    if err != nil {
        return "", err
    }
    return string(plaintext), nil
}
Secure File Storage
// Encrypt sensitive files before storing
func SecureUpload(file io.Reader, key [32]byte) error {
    nonce, _ := encaes.GenNonce()
    coder, _ := encaes.New(key, nonce)
    defer coder.Reset()
    
    // Encrypt stream
    encrypted := coder.EncodeReader(file)
    
    // Upload encrypted data
    return uploadToStorage(encrypted)
}
API Response Encryption
func EncryptedResponse(w http.ResponseWriter, data []byte, coder encoding.Coder) {
    encrypted := coder.Encode(data)
    
    w.Header().Set("Content-Type", "application/octet-stream")
    w.Header().Set("X-Encrypted", "AES-256-GCM")
    w.Write(encrypted)
}
Secure Message Queue
// Encrypt messages before publishing
func PublishSecure(msg []byte, coder encoding.Coder) error {
    encrypted := coder.Encode(msg)
    return messageQueue.Publish(encrypted)
}

// Decrypt messages after consuming
func ConsumeSecure(encrypted []byte, coder encoding.Coder) ([]byte, error) {
    return coder.Decode(encrypted)
}

Best Practices

1. Key Management
// ✅ Good: Load key from secure storage
key, err := loadKeyFromVault()

// ✅ Good: Generate new key for each session
key, err := encaes.GenKey()

// ❌ Bad: Hardcoded key in source
key := [32]byte{0x01, 0x02, ...}  // Never do this!

// ❌ Bad: Key in version control
const KEY = "my-secret-key"  // Never commit keys!
2. Nonce Usage
// ✅ Good: Generate new nonce per encryption
for _, msg := range messages {
    nonce, _ := encaes.GenNonce()
    coder, _ := encaes.New(key, nonce)
    encrypted := coder.Encode(msg)
    coder.Reset()
}

// ❌ Bad: Reusing nonce (catastrophic security failure!)
nonce, _ := encaes.GenNonce()
coder, _ := encaes.New(key, nonce)
for _, msg := range messages {
    encrypted := coder.Encode(msg)  // Same nonce!
}
3. Error Handling
// ✅ Good: Check all errors
plaintext, err := coder.Decode(encrypted)
if err != nil {
    log.Printf("Decryption failed: %v", err)
    return err
}

// ❌ Bad: Ignoring authentication errors
plaintext, _ := coder.Decode(encrypted)  // Might be tampered!
4. Resource Cleanup
// ✅ Good: Always reset coder
coder, _ := encaes.New(key, nonce)
defer coder.Reset()

// ❌ Bad: No cleanup
coder, _ := encaes.New(key, nonce)
encrypted := coder.Encode(data)
// Memory leak if coder holds resources
5. Secure Storage
// ✅ Good: Store keys in environment or vault
key := os.Getenv("ENCRYPTION_KEY")
// or use HashiCorp Vault, AWS Secrets Manager, etc.

// ✅ Good: Encrypted key storage
encryptedKey := loadFromFile("key.enc")
key := decryptKeyWithMasterKey(encryptedKey)

// ❌ Bad: Plain text key file
key := readFromFile("key.txt")  // Insecure!

Testing

Comprehensive testing documentation is available in TESTING.md.

Quick Test:

cd encoding/aes
go test -v -cover

Test Metrics:

  • 126 test specifications
  • 91.5% code coverage
  • Ginkgo v2 + Gomega framework
  • Edge case testing (invalid keys, corrupted data, etc.)

Contributing

Contributions are welcome! Please follow these guidelines:

Code Contributions

  • Do not use AI to generate package implementation code
  • AI may assist with tests, documentation, and bug fixing
  • All contributions must pass existing tests
  • Maintain or improve test coverage
  • Follow existing code style

Security

  • Report security vulnerabilities privately
  • Do not disclose security issues publicly
  • Follow responsible disclosure practices
  • Test cryptographic changes thoroughly

Testing

  • Write tests for all new features
  • Test edge cases (invalid input, corrupted data)
  • Verify authentication failures are detected
  • Include benchmarks for performance-critical code

Documentation

  • Update README.md for new features
  • Add security warnings where appropriate
  • Document all public APIs with GoDoc
  • Provide usage examples

See CONTRIBUTING.md for detailed guidelines.


Future Enhancements

Potential improvements for future versions:

Algorithm Support

  • ChaCha20-Poly1305 alternative (software-optimized)
  • AES-128-GCM option (faster, still secure)
  • Key derivation functions (PBKDF2, Argon2)

Features

  • Automatic key rotation
  • Nonce counter mode (deterministic nonces)
  • Streaming authentication without buffering
  • Multi-key support (key versioning)

Performance

  • Zero-copy encryption where possible
  • Batch encryption optimization
  • Parallel encryption for large files

Security

  • Key wrapping (encrypt-then-MAC for keys)
  • Secure memory wiping
  • Side-channel attack mitigation
  • FIPS 140-2 compliance mode

Suggestions and contributions are welcome via GitHub issues.


Cryptography
Security Resources

License

MIT License - See LICENSE file for details.

Copyright (c) 2023 Nicolas JUHEL


Resources


Security Disclosure

If you discover a security vulnerability, please email security@example.com (or create a private security advisory on GitHub). Do not disclose security issues publicly.


This package is part of the golib project.

Documentation

Overview

Package aes provides AES-256-GCM authenticated encryption with streaming I/O support.

This package implements the encoding.Coder interface for consistent encryption/decryption operations. It uses AES-256 with Galois/Counter Mode (GCM) for authenticated encryption, providing both confidentiality and integrity.

Features:

  • AES-256-GCM authenticated encryption
  • Cryptographically secure key and nonce generation
  • Streaming encryption/decryption via io.Reader interfaces
  • Hardware acceleration (AES-NI) on supported CPUs
  • Memory efficient operations
  • Thread-safe with separate coder instances

Security specifications:

  • Algorithm: AES-256 (256-bit key)
  • Mode: GCM (Galois/Counter Mode)
  • Nonce: 96 bits (12 bytes) - GCM standard
  • Authentication tag: 128 bits (16 bytes)

Example usage:

import encaes "github.com/nabbar/golib/encoding/aes"

// Generate key and nonce
key, _ := encaes.GenKey()
nonce, _ := encaes.GenNonce()

// Create coder
coder, _ := encaes.New(key, nonce)
defer coder.Reset()

// Encrypt
plaintext := []byte("Secret message")
ciphertext := coder.Encode(plaintext)

// Decrypt
decrypted, err := coder.Decode(ciphertext)
if err != nil {
    log.Fatal("Authentication failed")
}

Security warnings:

  • Never reuse a nonce with the same key (breaks GCM security)
  • Always check authentication errors during decryption
  • Store keys securely (never commit to version control)
  • Rotate keys periodically for high-security applications

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalidBufferSize = errors.New("invalid buffer size")
)

Functions

func GenKey

func GenKey() ([32]byte, error)

GenKey generates a cryptographically secure random 32-byte key.

It reads from the default random number generator and copies the resulting bytes into the key.

If there is an error while reading from the random number generator, this error will be returned.

The generated key is suitable for use with the AES package.

func GenNonce

func GenNonce() ([12]byte, error)

GenNonce generates a cryptographically secure random 12-byte nonce.

It reads from the default random number generator and copies the resulting bytes into the nonce.

If there is an error while reading from the random number generator, this error will be returned.

The generated nonce is suitable for use with the AES package.

func GetHexKey

func GetHexKey(s string) ([32]byte, error)

GetHexKey takes a hex encoded string and returns a 32 byte key. If the input string is less than 32 bytes, it will be zero-filled. If the input string is longer than 32 bytes, it will be truncated. If the input string is not a valid hex string, an error will be returned.

func GetHexNonce

func GetHexNonce(s string) ([12]byte, error)

GetHexNonce takes a hex encoded string and returns a 12-byte nonce. If the input string is less than 12 bytes, it will be zero-filled. If the input string is longer than 12 bytes, it will be truncated. If the input string is not a valid hex string, an error will be returned.

func New

func New(key [32]byte, nonce [12]byte) (libenc.Coder, error)

New creates a new AES coder instance.

The key must be either 16 bytes (AES-128) or 32 (AES-256). The nonce must be 12 bytes.

If the key or nonce is invalid, an error is returned. If there is an error while creating the AES cipher or the Galois/Counter Mode (GCM) cipher, this error is returned.

The returned coder instance is suitable for use with the EncodeReader and DecodeReader functions.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL