crypto

package module
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Aug 29, 2025 License: MPL-2.0 Imports: 12 Imported by: 0

README

go-crypto: Reusable AES-256-GCM cryptography library for Go.

an AGILira library

Originally developed for Nemesis, go-crypto provides secure and well-tested primitives for encryption, decryption, key and nonce management, and key fingerprinting using AES-256-GCM. Designed for clarity, reliability, and high code quality.

CI Security Go Report Card Coverage

Features

  • AES-256-GCM authenticated encryption
  • Argon2id key derivation (resistant to ASIC/FPGA attacks)
  • PBKDF2-SHA256 legacy support
  • Cryptographically secure random number generation
  • Secure memory zeroization for sensitive data
  • Cross-platform support (Windows, Linux, macOS)

Installation

go get github.com/agilira/go-crypto

Quick Examples

Basic Encryption/Decryption
import crypto "github.com/agilira/go-crypto"

key, err := crypto.GenerateKey()
if err != nil {
    // handle error
}

ciphertext, err := crypto.Encrypt("secret data", key)
if err != nil {
    // handle error
}

plaintext, err := crypto.Decrypt(ciphertext, key)
if err != nil {
    // handle error
}
Key Derivation with Argon2
password := []byte("my-secure-password")
salt := []byte("random-salt-123")

// Use secure defaults
key, err := crypto.DeriveKeyDefault(password, salt, 32)
if err != nil {
    // handle error
}
Key Import/Export
key, _ := crypto.GenerateKey()

// Export as base64
base64Key := crypto.KeyToBase64(key)

// Import from base64
restoredKey, err := crypto.KeyFromBase64(base64Key)
if err != nil {
    // handle error
}

// Securely wipe sensitive data
crypto.Zeroize(key)

Testing

go test

The library includes comprehensive test coverage with unit tests, integration tests, boundary condition testing, error path testing, concurrent access testing, and stress testing.

Documentation

Security

This library uses industry-standard cryptographic algorithms and follows security best practices. For detailed security information, see Security Documentation.

Security Tool Exclusions

Some static analysis rules are excluded with documented justification. See Security Documentation for details.

Performance

  • Optimized for typical use cases
  • Minimal memory allocations
  • Efficient base64 encoding/decoding
  • Fast key fingerprinting algorithm

Error Handling

All functions return standard Go errors for maximum compatibility. For advanced error handling with rich error details, the library integrates with github.com/agilira/go-errors.


go-crypto • an AGILira library

Documentation

Overview

Package crypto provides secure cryptographic utilities for Go applications.

This package offers a comprehensive set of cryptographic primitives including:

  • AES-256-GCM authenticated encryption and decryption
  • Argon2id key derivation for secure password-based key generation
  • PBKDF2-SHA256 legacy support for backward compatibility
  • Cryptographically secure random number generation
  • Key management utilities (import/export, validation, fingerprinting)
  • Secure memory zeroization for sensitive data

The package is designed for clarity, reliability, and high code quality, following Go best practices and security standards.

Quick Start

Basic encryption and decryption:

// Generate a new encryption key
key, err := crypto.GenerateKey()
if err != nil {
	log.Fatal(err)
}

// Encrypt some data
ciphertext, err := crypto.Encrypt("sensitive data", key)
if err != nil {
	log.Fatal(err)
}

// Decrypt the data
plaintext, err := crypto.Decrypt(ciphertext, key)
if err != nil {
	log.Fatal(err)
}

fmt.Println(plaintext) // Output: sensitive data

Key Derivation

For deriving keys from passwords:

password := []byte("my-secure-password")
salt := []byte("random-salt-123")

// Derive a key using Argon2id with secure defaults
derivedKey, err := crypto.DeriveKeyDefault(password, salt, 32)
if err != nil {
	log.Fatal(err)
}

// Use custom parameters for higher security
params := &crypto.KDFParams{
	Time:    4,    // 4 iterations
	Memory:  128,  // 128 MB memory
	Threads: 2,    // 2 threads
}
key, err := crypto.DeriveKey(password, salt, 32, params)

Key Management

Key utilities for import/export and validation:

// Generate and export a key
key, _ := crypto.GenerateKey()
base64Key := crypto.KeyToBase64(key)
hexKey := crypto.KeyToHex(key)

// Import and validate a key
importedKey, err := crypto.KeyFromBase64(base64Key)
if err != nil {
	log.Fatal(err)
}
err = crypto.ValidateKey(importedKey)
if err != nil {
	log.Fatal("Invalid key:", err)
}

// Generate a fingerprint for identification
fingerprint := crypto.GetKeyFingerprint(key)
fmt.Println("Key fingerprint:", fingerprint)

// Securely wipe sensitive data
crypto.Zeroize(key)

Error Handling

All functions return standard Go errors for maximum compatibility. For advanced error handling with rich error details, the library integrates with github.com/agilira/go-errors.

Example error handling:

ciphertext, err := crypto.Encrypt("data", key)
if err != nil {
	if errors.Is(err, crypto.ErrInvalidKeySize) {
		// Handle invalid key size
	} else if errors.Is(err, crypto.ErrEmptyPlaintext) {
		// Handle empty plaintext
	}
	// Handle other errors
}

Security Considerations

This library uses industry-standard cryptographic algorithms:

  • AES-256-GCM for authenticated encryption
  • Argon2id for key derivation (resistant to ASIC/FPGA attacks)
  • Cryptographically secure random number generation
  • Secure memory zeroization

For detailed security information, see the Security documentation.

Performance

The library is optimized for typical use cases with:

  • Minimal memory allocations
  • Efficient base64 encoding/decoding
  • Fast key fingerprinting algorithm
  • Configurable Argon2id parameters for security/performance balance

Copyright (c) 2025 AGILira Series: an AGLIra library SPDX-License-Identifier: MPL-2.0

Index

Constants

View Source
const (
	ErrCodeInvalidKey   = "CRYPTO_INVALID_KEY"
	ErrCodeEmptyPlain   = "CRYPTO_EMPTY_PLAINTEXT"
	ErrCodeCipherInit   = "CRYPTO_CIPHER_INIT"
	ErrCodeGCMInit      = "CRYPTO_GCM_INIT"
	ErrCodeNonceGen     = "CRYPTO_NONCE_GEN"
	ErrCodeBase64Decode = "CRYPTO_BASE64_DECODE"
	ErrCodeCipherShort  = "CRYPTO_CIPHERTEXT_SHORT"
	ErrCodeDecrypt      = "CRYPTO_DECRYPT"
)

Error codes for rich error handling

View Source
const (
	// DefaultTime is the default number of iterations for Argon2id.
	// Higher values increase security but also computation time.
	DefaultTime = 3

	// DefaultMemory is the default memory usage in MB for Argon2id.
	// Higher values increase security against memory-based attacks.
	DefaultMemory = 64

	// DefaultThreads is the default number of threads for Argon2id.
	// Should not exceed the number of CPU cores.
	DefaultThreads = 4
)

Default Argon2 parameters for key derivation. These values provide a good balance between security and performance.

View Source
const KeySize = 32

KeySize is the required key size for AES-256 encryption in bytes. AES-256 requires exactly 32 bytes (256 bits) for the encryption key.

Variables

View Source
var (
	// ErrInvalidKeySize is returned when the provided key is not exactly 32 bytes.
	ErrInvalidKeySize = errors.New("crypto: invalid key size")

	// ErrEmptyPlaintext is returned when trying to decrypt an empty string.
	// Note: Empty plaintext is supported for encryption.
	ErrEmptyPlaintext = errors.New("crypto: plaintext cannot be empty")

	// ErrCipherInit is returned when AES cipher initialization fails.
	ErrCipherInit = errors.New("crypto: cipher initialization error")

	// ErrGCMInit is returned when GCM mode initialization fails.
	ErrGCMInit = errors.New("crypto: GCM initialization error")

	// ErrNonceGen is returned when nonce generation fails.
	ErrNonceGen = errors.New("crypto: nonce generation error")

	// ErrBase64Decode is returned when base64 decoding fails.
	ErrBase64Decode = errors.New("crypto: base64 decode error")

	// ErrCiphertextShort is returned when the ciphertext is too short to contain a valid nonce.
	ErrCiphertextShort = errors.New("crypto: ciphertext too short")

	// ErrDecrypt is returned when decryption fails due to authentication failure or corruption.
	ErrDecrypt = errors.New("crypto: decryption error")
)

Public standard errors for drop-in compatibility. These errors can be used with errors.Is() for error checking.

Functions

func Decrypt

func Decrypt(encryptedText string, key []byte) (string, error)

Decrypt decrypts a base64-encoded ciphertext string using AES-256-GCM authenticated decryption.

This is a convenience wrapper around DecryptBytes that works with strings. The function verifies the authenticity of the ciphertext using the embedded authentication tag. If the ciphertext has been tampered with, the function will return an error.

Parameters:

  • encryptedText: The base64-encoded encrypted string (cannot be empty)
  • key: The 32-byte decryption key (must be exactly KeySize bytes)

Returns:

  • The decrypted plaintext string
  • An error if decryption fails (authentication failure, corruption, or invalid input)

Example:

key, _ := crypto.GenerateKey()
ciphertext, _ := crypto.Encrypt("sensitive data", key)
plaintext, err := crypto.Decrypt(ciphertext, key)
if err != nil {
	log.Fatal(err)
}
fmt.Println("Decrypted:", plaintext) // Output: sensitive data

The function will return an error if:

  • The key size is incorrect
  • The encrypted text is empty
  • The base64 decoding fails
  • The ciphertext is too short
  • Authentication fails (tampering detected)

func DecryptBytes

func DecryptBytes(encryptedText string, key []byte) ([]byte, error)

DecryptBytes decrypts a base64-encoded ciphertext string using AES-256-GCM authenticated decryption.

The function verifies the authenticity of the ciphertext using the embedded authentication tag. If the ciphertext has been tampered with, the function will return an error. This is the core decryption function that returns binary data.

Parameters:

  • encryptedText: The base64-encoded encrypted string (cannot be empty)
  • key: The 32-byte decryption key (must be exactly KeySize bytes)

Returns:

  • The decrypted plaintext as a byte slice
  • An error if decryption fails (authentication failure, corruption, or invalid input)

Example:

key, _ := crypto.GenerateKey()
data := []byte("sensitive binary data")
ciphertext, _ := crypto.EncryptBytes(data, key)
plaintext, err := crypto.DecryptBytes(ciphertext, key)
if err != nil {
	log.Fatal(err)
}
fmt.Println("Decrypted:", string(plaintext)) // Output: sensitive binary data

The function will return an error if:

  • The key size is incorrect
  • The encrypted text is empty
  • The base64 decoding fails
  • The ciphertext is too short
  • Authentication fails (tampering detected)

func DeriveKey

func DeriveKey(password, salt []byte, keyLen int, params *KDFParams) ([]byte, error)

DeriveKey derives a key from a password and salt using Argon2id (the recommended variant).

Argon2id is the recommended variant of Argon2, providing resistance against both side-channel attacks and time-memory trade-off attacks. It uses secure default parameters that provide strong protection against both CPU and memory-based attacks.

Parameters:

  • password: The password to derive the key from (cannot be empty)
  • salt: The salt to use for key derivation (cannot be empty, should be random)
  • keyLen: The desired length of the derived key in bytes (must be positive)
  • params: Custom Argon2id parameters (nil to use secure defaults)

Returns:

  • The derived key as a byte slice
  • An error if key derivation fails

Example:

password := []byte("my-secure-password")
salt := []byte("random-salt-123")

// Use secure defaults
key, err := crypto.DeriveKey(password, salt, 32, nil)
if err != nil {
	log.Fatal(err)
}

// Use custom parameters
params := &crypto.KDFParams{
	Time:    4,
	Memory:  128,
	Threads: 2,
}
key, err := crypto.DeriveKey(password, salt, 32, params)

If params is nil, secure defaults are used (Time: 3, Memory: 64MB, Threads: 4).

func DeriveKeyDefault

func DeriveKeyDefault(password, salt []byte, keyLen int) ([]byte, error)

DeriveKeyDefault derives a key using Argon2id with secure default parameters.

This is a convenience function for when you don't need custom parameters. It's equivalent to calling DeriveKey with params set to nil.

Parameters:

  • password: The password to derive the key from (cannot be empty)
  • salt: The salt to use for key derivation (cannot be empty, should be random)
  • keyLen: The desired length of the derived key in bytes (must be positive)

Returns:

  • The derived key as a byte slice
  • An error if key derivation fails

Example:

password := []byte("my-secure-password")
salt := []byte("random-salt-123")
key, err := crypto.DeriveKeyDefault(password, salt, 32)
if err != nil {
	log.Fatal(err)
}

func DeriveKeyPBKDF2 deprecated

func DeriveKeyPBKDF2(password, salt []byte, iterations, keyLen int) ([]byte, error)

DeriveKeyPBKDF2 derives a key using PBKDF2-SHA256 (deprecated).

This function is deprecated and kept only for backward compatibility. Use DeriveKey with Argon2id instead for better security against modern attacks. This function will be removed in a future version.

Parameters:

  • password: The password to derive the key from (cannot be empty)
  • salt: The salt to use for key derivation (cannot be empty, should be random)
  • iterations: The number of iterations (must be positive, recommend at least 100,000)
  • keyLen: The desired length of the derived key in bytes (must be positive)

Returns:

  • The derived key as a byte slice
  • An error if key derivation fails

Example:

password := []byte("my-secure-password")
salt := []byte("random-salt-123")
key, err := crypto.DeriveKeyPBKDF2(password, salt, 100000, 32)
if err != nil {
	log.Fatal(err)
}

Deprecated: Use DeriveKey instead for better security.

func DeriveKeyWithParams

func DeriveKeyWithParams(password, salt []byte, time, memoryMB, threads, keyLen int) ([]byte, error)

DeriveKeyWithParams derives a key from a password and salt using Argon2id with custom parameters.

This is a legacy function that provides direct parameter control. For new code, consider using DeriveKey with a KDFParams struct for better readability and maintainability.

Parameters:

  • password: The password to derive the key from (cannot be empty)
  • salt: The salt to use for key derivation (cannot be empty, should be random)
  • time: The number of iterations (must be positive)
  • memoryMB: The memory usage in MB (must be positive)
  • threads: The number of threads (must be positive)
  • keyLen: The desired length of the derived key in bytes (must be positive)

Returns:

  • The derived key as a byte slice
  • An error if key derivation fails

Example:

password := []byte("my-secure-password")
salt := []byte("random-salt-123")
key, err := crypto.DeriveKeyWithParams(password, salt, 4, 128, 2, 32)
if err != nil {
	log.Fatal(err)
}

Use this function only if you need to customize the parameters for specific requirements.

func Encrypt

func Encrypt(plaintext string, key []byte) (string, error)

Encrypt encrypts a plaintext string using AES-256-GCM authenticated encryption.

This is a convenience wrapper around EncryptBytes that works with strings. The function uses AES-256 in GCM mode, which provides both confidentiality and authenticity. The returned string is base64-encoded and contains the nonce, ciphertext, and authentication tag.

Parameters:

  • plaintext: The string to encrypt (can be empty)
  • key: The 32-byte encryption key (must be exactly KeySize bytes)

Returns:

  • A base64-encoded string containing the encrypted data
  • An error if encryption fails

Example:

key, _ := crypto.GenerateKey()
ciphertext, err := crypto.Encrypt("sensitive data", key)
if err != nil {
	log.Fatal(err)
}
fmt.Println("Encrypted:", ciphertext)

Empty plaintext is supported and will result in a valid ciphertext containing only the nonce and authentication tag.

func EncryptBytes

func EncryptBytes(plaintext []byte, key []byte) (string, error)

EncryptBytes encrypts a plaintext byte slice using AES-256-GCM authenticated encryption.

The function uses AES-256 in GCM mode, which provides both confidentiality and authenticity. The returned string is base64-encoded and contains the nonce, ciphertext, and authentication tag. This is the core encryption function that works with binary data.

Parameters:

  • plaintext: The byte slice to encrypt (can be empty)
  • key: The 32-byte encryption key (must be exactly KeySize bytes)

Returns:

  • A base64-encoded string containing the encrypted data
  • An error if encryption fails

Example:

key, _ := crypto.GenerateKey()
data := []byte("sensitive binary data")
ciphertext, err := crypto.EncryptBytes(data, key)
if err != nil {
	log.Fatal(err)
}
fmt.Println("Encrypted:", ciphertext)

Empty plaintext is supported and will result in a valid ciphertext containing only the nonce and authentication tag.

func GenerateKey

func GenerateKey() ([]byte, error)

GenerateKey generates a cryptographically secure random key of KeySize bytes.

This function creates a new 32-byte (256-bit) key suitable for AES-256 encryption. The key is generated using the cryptographically secure random number generator provided by the operating system.

Returns:

  • A 32-byte key as a byte slice
  • An error if key generation fails

Example:

key, err := crypto.GenerateKey()
if err != nil {
	log.Fatal(err)
}
fmt.Println("Generated key length:", len(key)) // Output: 32

The generated key is suitable for use with Encrypt and Decrypt functions.

func GenerateNonce

func GenerateNonce(size int) ([]byte, error)

GenerateNonce generates a cryptographically secure random nonce of the given size.

A nonce (number used once) is a random value that should be used only once for each encryption operation. This function generates a cryptographically secure random nonce suitable for use with AES-GCM encryption.

Parameters:

  • size: The desired size of the nonce in bytes (must be positive)

Returns:

  • A byte slice containing the random nonce
  • An error if nonce generation fails

Example:

nonce, err := crypto.GenerateNonce(12) // 12 bytes is standard for AES-GCM
if err != nil {
	log.Fatal(err)
}
fmt.Println("Generated nonce length:", len(nonce)) // Output: 12

For AES-GCM, a 12-byte nonce is recommended for optimal security and performance.

func GetKeyFingerprint

func GetKeyFingerprint(key []byte) string

GetKeyFingerprint generates a fingerprint for a key (non-cryptographic).

This function creates a short, human-readable identifier for a key by computing the SHA-256 hash and taking the first 8 bytes. This provides better collision resistance than using just the first few bytes of the key while maintaining speed.

The fingerprint is useful for logging, debugging, and identifying keys without exposing the actual key material.

Parameters:

  • key: The key to generate a fingerprint for

Returns:

  • A 16-character hexadecimal string representing the fingerprint
  • An empty string if the key is empty

Example:

key, _ := crypto.GenerateKey()
fingerprint := crypto.GetKeyFingerprint(key)
fmt.Println("Key fingerprint:", fingerprint) // e.g., "a1b2c3d4e5f67890"

Uses the first 8 bytes of SHA-256 for better collision resistance while maintaining speed.

func KeyFromBase64

func KeyFromBase64(s string) ([]byte, error)

KeyFromBase64 decodes a base64 string to a key.

This function is the inverse of KeyToBase64 and is useful for loading keys from text-based storage formats like JSON or configuration files.

Parameters:

  • s: The base64-encoded string to decode

Returns:

  • The decoded key as a byte slice
  • An error if the base64 decoding fails

Example:

base64Key := "dGVzdC1rZXktZGF0YS0xMjM0NTY3ODkwYWJjZGVm"
key, err := crypto.KeyFromBase64(base64Key)
if err != nil {
	log.Fatal(err)
}
fmt.Println("Decoded key length:", len(key))

func KeyFromHex

func KeyFromHex(s string) ([]byte, error)

KeyFromHex decodes a hexadecimal string to a key.

This function is the inverse of KeyToHex and is useful for loading keys from hexadecimal representations. The input string can contain both uppercase and lowercase hexadecimal characters.

Parameters:

  • s: The hexadecimal string to decode

Returns:

  • The decoded key as a byte slice
  • An error if the hexadecimal decoding fails

Example:

hexKey := "746573742d6b65792d646174612d31323334353637383930616263646566"
key, err := crypto.KeyFromHex(hexKey)
if err != nil {
	log.Fatal(err)
}
fmt.Println("Decoded key length:", len(key))

func KeyToBase64

func KeyToBase64(key []byte) string

KeyToBase64 encodes a key as a base64 string.

This function is useful for storing keys in text-based formats like JSON or configuration files. The returned string is safe to use in URLs and other text contexts.

Parameters:

  • key: The key to encode (can be any byte slice)

Returns:

  • A base64-encoded string representation of the key

Example:

key, _ := crypto.GenerateKey()
base64Key := crypto.KeyToBase64(key)
fmt.Println("Base64 key:", base64Key)

func KeyToHex

func KeyToHex(key []byte) string

KeyToHex encodes a key as a hexadecimal string.

This function is useful for displaying keys in a human-readable format or storing them in text-based formats. The returned string contains only lowercase hexadecimal characters (0-9, a-f).

Parameters:

  • key: The key to encode (can be any byte slice)

Returns:

  • A hexadecimal string representation of the key

Example:

key, _ := crypto.GenerateKey()
hexKey := crypto.KeyToHex(key)
fmt.Println("Hex key:", hexKey)

func ValidateKey

func ValidateKey(key []byte) error

ValidateKey checks that a key has the correct size for AES-256.

This function verifies that the provided key is exactly 32 bytes (256 bits), which is required for AES-256 encryption. It's useful for validating keys before using them with the Encrypt and Decrypt functions.

Parameters:

  • key: The key to validate

Returns:

  • An error if the key size is incorrect, nil if valid

Example:

key, _ := crypto.GenerateKey()
err := crypto.ValidateKey(key)
if err != nil {
	log.Fatal("Invalid key:", err)
}
fmt.Println("Key is valid for AES-256")

The function will return an error if the key is not exactly 32 bytes.

func Zeroize

func Zeroize(b []byte)

Zeroize securely wipes a byte slice from memory.

This function overwrites all bytes in the slice with zeros to prevent sensitive data from remaining in memory after use. This is important for security when dealing with cryptographic keys and other sensitive data.

Note: This function modifies the original slice in place.

Parameters:

  • b: The byte slice to zeroize

Example:

key, _ := crypto.GenerateKey()
// Use the key for encryption/decryption
ciphertext, _ := crypto.Encrypt("data", key)
// Securely wipe the key from memory
crypto.Zeroize(key)

Types

type KDFParams

type KDFParams struct {
	// Time is the number of iterations for Argon2id.
	// Higher values increase security but also computation time.
	// If zero, DefaultTime is used.
	Time uint32 `json:"time,omitempty"`

	// Memory is the memory usage in MB for Argon2id.
	// Higher values increase security against memory-based attacks.
	// If zero, DefaultMemory is used.
	Memory uint32 `json:"memory,omitempty"`

	// Threads is the number of threads for Argon2id.
	// Should not exceed the number of CPU cores.
	// If zero, DefaultThreads is used.
	Threads uint8 `json:"threads,omitempty"`
}

KDFParams defines custom parameters for Argon2id key derivation.

If a field is zero, the library's secure default will be used. This allows for flexible configuration while maintaining security.

Example:

// Use custom parameters
params := &crypto.KDFParams{
	Time:    4,    // 4 iterations
	Memory:  128,  // 128 MB memory
	Threads: 2,    // 2 threads
}
key, err := crypto.DeriveKey(password, salt, 32, params)

// Use secure defaults (pass nil)
key, err := crypto.DeriveKey(password, salt, 32, nil)

Jump to

Keyboard shortcuts

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