encryption

package
v1.25.0 Latest Latest
Warning

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

Go to latest
Published: May 19, 2026 License: MIT Imports: 7 Imported by: 0

README ΒΆ

encryption/ - Type-Safe Data Encryption

Multi-algorithm encryption module with envelope encryption, CMS support, and OpenSSL compatibility.

Test Coverage

Overview

The encryption module is the most sophisticated module in GoPKI, providing:

  • Multiple encryption algorithms (RSA-OAEP, ECDH+AES-GCM, X25519+AES-GCM)
  • Envelope encryption for large data and multi-recipient scenarios
  • Certificate-based encryption with PKI integration
  • CMS (RFC 5652) format support for interoperability
  • OpenSSL compatibility with optional OpenSSL-compatible mode

Test Coverage: 89.1% (highest in GoPKI)

πŸ€– AI Agent Quick Start

File Structure Map
encryption/
β”œβ”€β”€ encryption.go                 - High-level API and convenience functions
β”‚                                  Core encryption types and options
β”‚                                  Helper functions (EncryptForCertificate, DecryptWithKeyPair)
β”‚                                  Option builders and defaults
β”‚
β”œβ”€β”€ cms.go                        - CMS format encoding/decoding
β”‚                                  CMS encoding (EncodeToCMS)
β”‚                                  CMS decoding with auto-detection
β”‚                                  Format detection and validation
β”‚
β”œβ”€β”€ asymmetric/                   - Asymmetric encryption implementations
β”‚   β”œβ”€β”€ asymmetric.go            - **START HERE**: Core asymmetric logic
β”‚   β”‚                             Encryptor/Decryptor interfaces
β”‚   β”‚                             Algorithm dispatching and routing
β”‚   β”‚                             Validation and helpers, error handling
β”‚   β”œβ”€β”€ rsa.go                   - RSA-OAEP encryption
β”‚   β”‚                             RSA encryptor implementation
β”‚   β”‚                             RSA-OAEP padding and parameters
β”‚   β”œβ”€β”€ ecdsa.go                 - ECDH + AES-GCM encryption
β”‚   β”‚                             ECDH key agreement
β”‚   β”‚                             AES-GCM encryption with derived key, curve validation
β”‚   β”œβ”€β”€ ed25519.go               - X25519 + AES-GCM encryption
β”‚   β”‚                             X25519 key derivation, AES-GCM encryption
β”‚   β”‚                             Public-key-only limitation handling
β”‚   β”œβ”€β”€ helpers.go               - Shared encryption utilities
β”‚   └── *_test.go                - Comprehensive asymmetric tests
β”‚
β”œβ”€β”€ symmetric/                    - Symmetric encryption
β”‚   β”œβ”€β”€ symmetric.go             - AES-GCM implementation
β”‚   β”‚                             AES-GCM encryption/decryption
β”‚   β”‚                             Key derivation (for envelope)
β”‚   β”‚                             Nonce generation and validation
β”‚   └── symmetric_test.go        - AES-GCM tests
β”‚
β”œβ”€β”€ envelope/                     - Envelope encryption - CRITICAL for large data
β”‚   β”œβ”€β”€ envelope.go              - **MOST COMPLEX**: Hybrid encryption
β”‚   β”‚                             Envelope structure and types
β”‚   β”‚                             Envelope creation (DEK + KEK pattern)
β”‚   β”‚                             Multi-recipient support
β”‚   β”‚                             OpenSSL-compatible mode
β”‚   β”‚                             Decryption and unwrapping
β”‚   β”œβ”€β”€ envelope_test.go         - Envelope encryption tests
β”‚   └── cms_cycle_test.go        - **CRITICAL**: CMS cycle tests
β”‚                                  Tests the full encrypt β†’ encode β†’ decode β†’ decrypt cycle
β”‚
└── certificate/                  - Certificate-based workflows
    β”œβ”€β”€ certificate.go           - Certificate-based encryption API
    β”‚                             Certificate extraction and validation
    β”‚                             High-level certificate encryption
    β”‚                             Certificate chain handling
    └── certificate_test.go      - Certificate encryption tests
Key Functions Location
Function File Purpose
EncryptForCertificate() encryption.go Primary API - Encrypt for certificate recipient
DecryptWithKeyPair() encryption.go Primary API - Decrypt with key pair
EncodeToCMS() cms.go Encode to CMS/PKCS#7 format
DecodeFromCMS() cms.go Decode from CMS with auto-detection
DecodeDataWithKey() cms.go Key function - Decode and decrypt CMS data
Envelope Encryption
EncryptWithCertificate() envelope/envelope.go Create envelope for certificate
Encrypt() (envelope) envelope/envelope.go Core envelope - DEK + KEK pattern
Decrypt() (envelope) envelope/envelope.go Unwrap envelope and decrypt
OpenSSL Compatible Mode
OpenSSLCompatible option envelope/envelope.go Enable OpenSSL smime compatibility
OpenSSL format detection cms.go Auto-detect OpenSSL format
Asymmetric Algorithms
EncryptWithRSA() asymmetric/rsa.go RSA-OAEP encryption (≀190 bytes)
EncryptWithECDSA() asymmetric/ecdsa.go ECDH + AES-GCM (unlimited size)
EncryptWithEd25519() asymmetric/ed25519.go X25519 + AES-GCM (unlimited size)
Common Modification Points

Adding New Encryption Algorithm:

  1. Create asymmetric/newalgo.go following asymmetric/rsa.go pattern
  2. Implement Encryptor and Decryptor interfaces (see asymmetric/asymmetric.go:50-150)
  3. Add algorithm constant in encryption.go:50-100
  4. Update algorithm dispatcher in asymmetric/asymmetric.go:180-350
  5. Add comprehensive tests following asymmetric/rsa_test.go patterns
  6. Update envelope support in envelope/envelope.go:380-500

Adding OpenSSL Compatibility Feature:

  1. Read OpenSSL compatibility implementation in envelope/envelope.go:520-600
  2. Review existing OpenSSL test in compatibility/encryption/encryption_test.go:350-450
  3. Understand CMS auto-detection in cms.go:160-180
  4. Add new OpenSSL command integration in compatibility/helpers.go
  5. Test bidirectional compatibility (OpenSSL β†’ GoPKI and GoPKI β†’ OpenSSL)
  6. Document in COMPATIBILITY_REPORT.md and docs/OPENSSL_COMPAT.md

Fixing Envelope Encryption Bug:

  1. Start with tests - Read envelope/cms_cycle_test.go (lines 56-155)
  2. Understand the full cycle: Encrypt β†’ EncodeToCMS β†’ DecodeFromCMS β†’ Decrypt
  3. Check envelope structure preservation in envelope/envelope.go:50-150
  4. Verify DEK (Data Encryption Key) and KEK (Key Encryption Key) handling
  5. Run: task test:specific -- TestCertificateEnvelopeEncryptionWithCMSCycle
  6. Test with OpenSSL: task test:compatibility

Common Bug Patterns:

  • CMS cycle breaks structure: Check DecodeFromCMS doesn't decrypt prematurely
  • Multi-recipient fails: Verify Recipients array is preserved
  • OpenSSL incompatibility: Check PKCS#7 EnvelopedData format compliance
  • Memory issues with large data: Use envelope encryption, not direct RSA
Type Relationships
// Core encryption types (encryption.go:50-150)

// Encryption algorithm identifiers
type EncryptionAlgorithm string
const (
    AlgorithmRSAOAEP    EncryptionAlgorithm = "rsa-oaep"
    AlgorithmECDH       EncryptionAlgorithm = "ecdh-aes-gcm"
    AlgorithmX25519     EncryptionAlgorithm = "x25519-aes-gcm"
    AlgorithmEnvelope   EncryptionAlgorithm = "envelope"
)

// Primary encryption data structure
type EncryptedData struct {
    Algorithm   EncryptionAlgorithm    // Which algorithm was used
    Format      EncryptionFormat       // raw, pkcs7, cms
    Data        []byte                 // Encrypted data (or PKCS#7 EnvelopedData)
    Recipients  []*RecipientInfo       // For envelope encryption
    IV          []byte                 // Initialization vector (AES-GCM)
    Tag         []byte                 // Authentication tag (AES-GCM)
    Metadata    map[string]any         // Additional metadata
}

// Recipient information for envelope encryption
type RecipientInfo struct {
    Certificate            *x509.Certificate    // Recipient's certificate
    KeyEncryptionAlgorithm EncryptionAlgorithm  // How KEK is encrypted
    EncryptedKey           []byte               // Encrypted DEK
}

// Encryption options
type EncryptOptions struct {
    Algorithm          EncryptionAlgorithm  // Algorithm to use
    OpenSSLCompatible  bool                 // Enable OpenSSL smime compatibility
    Metadata           map[string]any       // Custom metadata
}

// Integration with keypair module:
func EncryptForCertificate(data []byte, cert *x509.Certificate, opts EncryptOptions) (*EncryptedData, error)
func DecryptWithKeyPair[T keypair.KeyPair](encData *EncryptedData, keyPair T) ([]byte, error)

Critical Concepts:

Envelope Encryption Pattern (DEK + KEK):

1. Generate random Data Encryption Key (DEK) - 32 bytes for AES-256
2. Encrypt data with DEK using AES-GCM
3. Encrypt DEK with recipient's public key (Key Encryption Key - KEK)
4. Store encrypted data + encrypted DEK + IV + Tag
5. Recipient decrypts DEK with private key, then decrypts data with DEK

OpenSSL Compatible Mode:

opts := encryption.DefaultEncryptOptions()
opts.OpenSSLCompatible = true  // Create standard PKCS#7 EnvelopedData

// This format can be decrypted with:
// openssl smime -decrypt -in encrypted.p7 -inkey private.pem -out decrypted.txt

// And GoPKI can decrypt OpenSSL smime encrypted data:
// openssl smime -encrypt -aes256 -binary -in data.txt -out encrypted.p7 cert.pem
// decoded := encryption.DecodeDataWithKey(cmsData, cert, privateKey)
Dependencies

This module depends on:

  • keypair/ - Key type constraints for encryption operations
  • cert/ - Certificate handling for certificate-based encryption
  • crypto/aes - AES symmetric encryption
  • crypto/cipher - Cipher modes (GCM)
  • crypto/rsa - RSA encryption
  • crypto/ecdsa - ECDSA key agreement
  • crypto/ed25519 - Ed25519 key derivation
  • golang.org/x/crypto/curve25519 - X25519 key agreement
  • go.mozilla.org/pkcs7 - CMS/PKCS#7 format support

Modules that depend on THIS:

  • None (encryption is a leaf module)

External Tool Integration:

  • OpenSSL smime command - For envelope encryption interoperability
  • OpenSSL enc command - For symmetric encryption testing
Testing Strategy

Test Files:

  • encryption_test.go - High-level API tests (440 lines)
  • cms_test.go - CMS format tests (116 lines)
  • cms_generic_test.go - Generic CMS tests (170 lines)
  • asymmetric/asymmetric_test.go - Core asymmetric tests (883 lines)
  • asymmetric/rsa_test.go - RSA-specific tests (323 lines)
  • asymmetric/ecdsa_test.go - ECDSA-specific tests (416 lines)
  • asymmetric/ed25519_test.go - Ed25519-specific tests (457 lines)
  • asymmetric/helpers_test.go - Utility tests (496 lines)
  • symmetric/symmetric_test.go - AES-GCM tests (726 lines)
  • envelope/envelope_test.go - Envelope encryption tests (659 lines)
  • envelope/cms_cycle_test.go - CRITICAL: CMS cycle tests (342 lines)
  • certificate/certificate_test.go - Certificate workflow tests (621 lines)
  • compatibility/encryption/encryption_test.go - OpenSSL compatibility

Running Tests:

# All encryption module tests
go test ./encryption/...

# Specific submodules
go test ./encryption/envelope/...
go test ./encryption/asymmetric/...
go test ./encryption/symmetric/...

# Critical CMS cycle test
task test:specific -- TestCertificateEnvelopeEncryptionWithCMSCycle

# OpenSSL compatibility
task test:compatibility
cd compatibility/encryption && go test -tags=compatibility -v

Test Coverage: 89.1% (6,034 lines of tests, highest in GoPKI)


Features

Encryption Algorithms
Algorithm Data Size Limit Key Agreement Speed Use Case
RSA-OAEP ~190 bytes (2048-bit) ❌ Fast Small data, maximum compatibility
ECDH + AES-GCM Unlimited βœ… Fast Large data, modern systems
X25519 + AES-GCM Unlimited βœ… Fastest High performance, Ed25519 keys
Envelope Unlimited βœ… Optimal Large data, multi-recipient
Envelope Encryption

Hybrid encryption combining asymmetric and symmetric cryptography:

  • Generate random DEK (Data Encryption Key) for AES-256-GCM
  • Encrypt data with DEK (fast symmetric encryption)
  • Encrypt DEK with recipient's public key (secure key transport)
  • Support multiple recipients (each gets their own encrypted DEK)
OpenSSL Compatibility

OpenSSL smime Interoperability:

  • βœ… GoPKI can decrypt OpenSSL smime encrypted data (auto-detected)
  • βœ… OpenSSL can decrypt GoPKI encrypted data (with OpenSSLCompatible mode)
  • βœ… Standard PKCS#7 EnvelopedData format
  • ⚠️ RSA only - OpenSSL smime doesn't support ECDSA/Ed25519 envelope encryption

Installation

go get github.com/jasoet/gopki/encryption

Quick Start

package main

import (
    "github.com/jasoet/gopki/encryption"
    "github.com/jasoet/gopki/cert"
    "github.com/jasoet/gopki/keypair/algo"
)

func main() {
    // Setup: Generate key pair and certificate
    keyPair, _ := algo.GenerateRSAKeyPair(algo.KeySize2048)
    certificate, _ := cert.CreateSelfSignedCertificate(keyPair, cert.CertificateRequest{...})

    // Encrypt data for certificate recipient
    data := []byte("Confidential message")
    encrypted, _ := encryption.EncryptForCertificate(
        data,
        certificate.Certificate,
        encryption.DefaultEncryptOptions(),
    )

    // Decrypt with private key
    decrypted, _ := encryption.DecryptWithKeyPair(encrypted, keyPair)

    // decrypted == data βœ…
}
Envelope Encryption for Large Data
package main

import (
    "github.com/jasoet/gopki/encryption"
    "github.com/jasoet/gopki/encryption/envelope"
)

func main() {
    // Large data (GBs supported)
    largeData := []byte("Very large document...")

    // Encrypt with envelope (hybrid encryption)
    opts := encryption.DefaultEncryptOptions()
    encrypted, _ := envelope.EncryptWithCertificate(largeData, certificate, opts)

    // Encode to CMS format for storage/transmission
    cmsData, _ := encryption.EncodeToCMS(encrypted)

    // Later... decode and decrypt
    decoded, _ := encryption.DecodeDataWithKey(cmsData, certificate.Certificate, keyPair.PrivateKey)
    decrypted, _ := envelope.Decrypt(decoded, keyPair, encryption.DefaultDecryptOptions())

    // decrypted == largeData βœ…
}
OpenSSL Compatible Mode
package main

import (
    "github.com/jasoet/gopki/encryption"
    "github.com/jasoet/gopki/encryption/envelope"
    "os"
)

func main() {
    // Enable OpenSSL compatibility
    opts := encryption.DefaultEncryptOptions()
    opts.OpenSSLCompatible = true  // Creates standard PKCS#7 EnvelopedData

    // Encrypt with OpenSSL-compatible format
    encrypted, _ := envelope.EncryptWithCertificate(data, certificate, opts)

    // Save CMS data
    cmsData, _ := encryption.EncodeToCMS(encrypted)
    os.WriteFile("encrypted.p7", cmsData, 0644)

    // Now OpenSSL can decrypt:
    // openssl smime -decrypt -in encrypted.p7 -inkey private.pem -out decrypted.txt
}
Multi-Recipient Encryption
package main

import (
    "github.com/jasoet/gopki/encryption"
    "github.com/jasoet/gopki/encryption/envelope"
)

func main() {
    // Multiple recipients
    recipients := []*x509.Certificate{cert1, cert2, cert3}

    // Create envelope for all recipients
    data := []byte("Shared secret")
    encrypted, _ := envelope.CreateEnvelope(data, recipients, encryption.DefaultEncryptOptions())

    // Any recipient can decrypt with their private key
    decrypted1, _ := envelope.Decrypt(encrypted, keyPair1, encryption.DefaultDecryptOptions())
    decrypted2, _ := envelope.Decrypt(encrypted, keyPair2, encryption.DefaultDecryptOptions())
    decrypted3, _ := envelope.Decrypt(encrypted, keyPair3, encryption.DefaultDecryptOptions())

    // All get the same plaintext βœ…
}

API Reference

High-Level API
// Primary encryption API
func EncryptForCertificate(data []byte, cert *x509.Certificate, opts EncryptOptions) (*EncryptedData, error)

// Primary decryption API
func DecryptWithKeyPair[T keypair.KeyPair](encData *EncryptedData, keyPair T) ([]byte, error)

// CMS format operations
func EncodeToCMS(data *EncryptedData) ([]byte, error)
func DecodeFromCMS[T any](cmsData []byte, cert *x509.Certificate, privateKey T) (*EncryptedData, error)
func DecodeDataWithKey[T keypair.PrivateKey](data []byte, cert *x509.Certificate, privateKey T) (*EncryptedData, error)

// Options
func DefaultEncryptOptions() EncryptOptions
func DefaultDecryptOptions() DecryptOptions
Envelope Encryption API
// Envelope encryption for single recipient
func envelope.EncryptWithCertificate(data []byte, cert *cert.Certificate, opts EncryptOptions) (*EncryptedData, error)

// Envelope encryption for multiple recipients
func envelope.CreateEnvelope(data []byte, certs []*x509.Certificate, opts EncryptOptions) (*EncryptedData, error)

// Decrypt envelope
func envelope.Decrypt[T keypair.KeyPair](encData *EncryptedData, keyPair T, opts DecryptOptions) ([]byte, error)
Asymmetric Encryption API
// RSA-OAEP encryption (≀190 bytes for RSA-2048)
func asymmetric.EncryptWithRSA(data []byte, keyPair *algo.RSAKeyPair, opts EncryptOptions) (*EncryptedData, error)
func asymmetric.DecryptWithRSA(encData *EncryptedData, keyPair *algo.RSAKeyPair, opts DecryptOptions) ([]byte, error)

// ECDH + AES-GCM encryption (unlimited size)
func asymmetric.EncryptWithECDSA(data []byte, keyPair *algo.ECDSAKeyPair, opts EncryptOptions) (*EncryptedData, error)
func asymmetric.DecryptWithECDSA(encData *EncryptedData, keyPair *algo.ECDSAKeyPair, opts DecryptOptions) ([]byte, error)

// X25519 + AES-GCM encryption (unlimited size)
func asymmetric.EncryptWithEd25519(data []byte, keyPair *algo.Ed25519KeyPair, opts EncryptOptions) (*EncryptedData, error)
func asymmetric.DecryptWithEd25519(encData *EncryptedData, keyPair *algo.Ed25519KeyPair, opts DecryptOptions) ([]byte, error)
Symmetric Encryption API
// AES-256-GCM encryption (internal use, exposed for advanced scenarios)
func symmetric.EncryptAESGCM(data []byte, key []byte) (*EncryptedData, error)
func symmetric.DecryptAESGCM(encData *EncryptedData, key []byte) ([]byte, error)

Algorithm Selection Guide

When to Use Each Algorithm

RSA-OAEP:

  • βœ… Small data (≀190 bytes for 2048-bit keys)
  • βœ… Maximum compatibility
  • βœ… No key agreement needed
  • ❌ Large data (use envelope instead)

ECDH + AES-GCM:

  • βœ… Unlimited data size
  • βœ… Modern cryptography
  • βœ… Smaller keys than RSA
  • βœ… Good performance
  • Use for ECDSA key pairs

X25519 + AES-GCM:

  • βœ… Unlimited data size
  • βœ… Fastest performance
  • βœ… Smallest keys
  • βœ… Memory efficient
  • Use for Ed25519 key pairs

Envelope Encryption:

  • βœ… Large files (GBs)
  • βœ… Multiple recipients
  • βœ… Optimal performance
  • βœ… OpenSSL compatibility
  • Recommended for most use cases
Decision Tree
Need to encrypt data?
β”‚
β”œβ”€ Data > 190 bytes?
β”‚  └─ YES β†’ Use Envelope Encryption
β”‚
β”œβ”€ Multiple recipients?
β”‚  └─ YES β†’ Use Envelope Encryption
β”‚
β”œβ”€ OpenSSL compatibility needed?
β”‚  └─ YES β†’ Use Envelope with OpenSSLCompatible=true
β”‚
β”œβ”€ Ed25519 keys?
β”‚  └─ YES β†’ Use X25519 + AES-GCM or Envelope
β”‚
β”œβ”€ ECDSA keys?
β”‚  └─ YES β†’ Use ECDH + AES-GCM or Envelope
β”‚
└─ RSA keys + small data?
   └─ YES β†’ Use RSA-OAEP or Envelope

General Recommendation: Use Envelope Encryption for all scenarios unless you have a specific reason not to.

Security Features

Cryptographic Security
  • AES-256-GCM: Authenticated encryption with associated data (AEAD)
  • Strong Random: Uses crypto/rand.Reader for all random generation
  • Key Derivation: Proper key agreement protocols (ECDH, X25519)
  • Authentication Tags: GCM provides integrity and authenticity
  • Secure Padding: RSA-OAEP with SHA-256
Implementation Security
  • No Key Reuse: Fresh DEKs for every envelope encryption
  • IV/Nonce Uniqueness: Randomly generated IVs for each encryption
  • Constant-Time Operations: Where possible (Ed25519, GCM)
  • Type Safety: Generic constraints prevent runtime errors
  • Memory Safety: No raw key material exposure
OpenSSL Compatibility Security
// OpenSSL-compatible mode creates standard PKCS#7 EnvelopedData
opts.OpenSSLCompatible = true

// Security considerations:
// βœ… Uses AES-256-CBC (OpenSSL standard)
// βœ… Standard PKCS#7 padding
// βœ… RSA-OAEP or PKCS#1 v1.5 for KEK encryption
// ⚠️ RSA certificates only (OpenSSL smime limitation)
// ⚠️ No AEAD in CBC mode (integrity from envelope structure)

Performance Characteristics

Encryption Performance
Algorithm 1KB 1MB 100MB Notes
RSA-OAEP ~1ms N/A N/A Size limited
ECDH+AES ~2ms ~15ms ~1.5s Key agreement + AES
X25519+AES ~1ms ~12ms ~1.2s Fastest
Envelope ~2ms ~15ms ~1.5s Optimal for large data
Memory Usage
  • RSA-OAEP: Minimal overhead (~2KB)
  • ECDH/X25519: Ephemeral key pair (~200 bytes)
  • Envelope: DEK (32 bytes) + minimal overhead
  • Large Data: Streaming capable (constant memory)

Error Handling

Common Errors
// Data too large for RSA-OAEP
err := encryption.ErrDataTooLarge
// Solution: Use envelope encryption or ECDH/X25519

// Invalid certificate
err := encryption.ErrInvalidCertificate
// Solution: Verify certificate validity and key usage

// Decryption failure
err := encryption.ErrDecryptionFailed
// Solution: Check key pair matches encryption certificate

// Unsupported algorithm
err := encryption.ErrUnsupportedAlgorithm
// Solution: Check algorithm compatibility

// OpenSSL mode with non-RSA key
err := encryption.ErrOpenSSLRequiresRSA
// Solution: Use RSA certificate or disable OpenSSLCompatible mode

OpenSSL Integration

Encrypting with OpenSSL, Decrypting with GoPKI
# OpenSSL encrypts
openssl smime -encrypt -aes256 -binary -in plaintext.txt -out encrypted.p7 certificate.pem
// GoPKI decrypts
cmsData, _ := os.ReadFile("encrypted.p7")
decoded, _ := encryption.DecodeDataWithKey(cmsData, certificate, privateKey)
plaintext := decoded.Data  // Auto-detected and decrypted!
Encrypting with GoPKI, Decrypting with OpenSSL
// GoPKI encrypts with OpenSSL-compatible mode
opts := encryption.DefaultEncryptOptions()
opts.OpenSSLCompatible = true

encrypted, _ := envelope.EncryptWithCertificate(data, certificate, opts)
cmsData, _ := encryption.EncodeToCMS(encrypted)
os.WriteFile("encrypted.p7", cmsData, 0644)
# OpenSSL decrypts
openssl smime -decrypt -in encrypted.p7 -inkey private.pem -out decrypted.txt
Limitations
  • OpenSSL smime only supports RSA certificates for envelope encryption
  • ECDSA and Ed25519 envelope encryption are GoPKI-only features
  • OpenSSL CBC mode vs GoPKI GCM mode (different security properties)

Testing

Run Tests
# All encryption tests
go test ./encryption/...

# Specific submodules
go test ./encryption/envelope/... -v
go test ./encryption/asymmetric/... -v

# Critical CMS cycle test
task test:specific -- TestCertificateEnvelopeEncryptionWithCMSCycle

# OpenSSL compatibility tests
task test:compatibility
cd compatibility/encryption && go test -tags=compatibility -v

# Specific OpenSSL test
task test:specific -- TestOpenSSLEnvelopeCompatibility
Test Coverage: 89.1%

Comprehensive Test Suite:

  • Unit tests for all algorithms
  • Integration tests for workflows
  • CMS format round-trip tests
  • OpenSSL compatibility tests
  • Edge cases and error conditions
  • Performance benchmarks

Troubleshooting

Issue: Data too large for RSA

Problem: ErrDataTooLarge when encrypting with RSA-OAEP

Solution: Use envelope encryption:

// Instead of:
encrypted, err := asymmetric.EncryptWithRSA(largeData, keyPair, opts)

// Use envelope:
encrypted, err := envelope.EncryptWithCertificate(largeData, certificate, opts)
Issue: OpenSSL can't decrypt GoPKI data

Problem: OpenSSL smime fails to decrypt

Solution: Enable OpenSSL-compatible mode:

opts := encryption.DefaultEncryptOptions()
opts.OpenSSLCompatible = true  // ← Add this
encrypted, _ := envelope.EncryptWithCertificate(data, certificate, opts)
Issue: GoPKI can't decrypt OpenSSL data

Problem: Decryption fails with OpenSSL-encrypted data

Solution: Use DecodeDataWithKey() which auto-detects format:

// This auto-detects and handles OpenSSL format
decoded, _ := encryption.DecodeDataWithKey(cmsData, certificate, privateKey)
plaintext := decoded.Data  // Already decrypted
Issue: Ed25519 certificate encryption fails

Problem: ErrEd25519CertificateEncryptionNotSupported

Solution: Ed25519 has limitations with public-key-only encryption. Use full key pair:

// Certificate-based (not supported for Ed25519)
encrypted, err := encryption.EncryptForCertificate(data, ed25519Cert, opts)
// Error: Ed25519 requires full key pair

// Key-pair based (supported)
encrypted, err := asymmetric.EncryptWithEd25519(data, ed25519KeyPair, opts)
// βœ… Works!

Best Practices

  1. Use Envelope Encryption by Default: Optimal for most scenarios
  2. Enable OpenSSL Mode When Needed: For interoperability with OpenSSL tools
  3. Validate Certificates: Always verify certificate validity before encryption
  4. Use Type-Safe APIs: Prefer high-level EncryptForCertificate() and DecryptWithKeyPair()
  5. Handle Errors Properly: Check for ErrDataTooLarge, ErrInvalidCertificate, etc.
  6. Test OpenSSL Compatibility: Use compatibility tests for production OpenSSL integration
  7. Choose Right Algorithm: See algorithm selection guide above

Further Reading

License

MIT License - see LICENSE file


Part of GoPKI - Type-Safe Cryptography for Production

Documentation ΒΆ

Overview ΒΆ

File cms.go implements Cryptographic Message Syntax (CMS) format support using external Mozilla PKCS7 library for reliable and standards-compliant implementation.

This replaces the previous complex manual ASN.1 implementation with a battle-tested external library, reducing complexity and maintenance burden while improving security and standards compliance.

Standards compliance:

  • RFC 5652: Cryptographic Message Syntax (CMS)
  • PKCS#7: Cryptographic Message Syntax (legacy compatibility)
  • AES-256-GCM: Authenticated encryption
  • RSA-OAEP: Key transport mechanism

Features:

  • Envelope encryption for multiple recipients
  • Certificate-based encryption and decryption
  • AES-256-GCM authenticated encryption (default)
  • Standards-compliant ASN.1 DER encoding
  • Simplified and maintainable codebase

Security:

  • Authenticated encryption with AES-GCM
  • RSA-OAEP for secure key transport
  • Certificate validation during decryption
  • No manual ASN.1 parsing (reduces attack surface)

Migration note:

The API signature for DecodeFromCMS has changed to require certificate
and private key parameters for proper decryption. This is more secure
and explicit than the previous implementation.

Package encryption provides comprehensive, type-safe data encryption and decryption functionality that seamlessly integrates with the GoPKI keypair and certificate infrastructure.

This package extends the GoPKI ecosystem by providing production-ready encryption capabilities using the same type-safe design patterns. It supports multiple cryptographic algorithms, various data sizes, and different output formats while maintaining compatibility with existing PKI workflows.

Key Features:

  • Type-safe encryption with Go generics integration
  • Multiple encryption algorithms with automatic selection
  • Support for both small and large data encryption
  • Certificate-based encryption workflows
  • Configurable output formats (Raw, PKCS#7, CMS)
  • Envelope encryption for large data sets
  • Comprehensive error handling and validation

Supported Algorithms:

  • RSA-OAEP: Direct RSA encryption (recommended for keys β‰₯2048 bits)
  • ECDH + AES-GCM: ECDSA key agreement with symmetric encryption
  • X25519 + AES-GCM: Ed25519-based key agreement with symmetric encryption
  • AES-GCM: Direct symmetric encryption for envelope encryption

Data Size Recommendations:

  • Small data (≀190 bytes for RSA-2048): Direct RSA-OAEP encryption
  • Medium data (≀8KB): ECDH/X25519 + AES-GCM
  • Large data (>8KB): Envelope encryption (recommended)

Output Formats:

  • Raw: Binary format with magic bytes for format identification
  • PKCS#7: Standard ASN.1 DER-encoded format
  • CMS: Cryptographic Message Syntax format

Security Considerations:

  • All encryption uses authenticated encryption (AES-GCM)
  • RSA-OAEP provides semantic security for RSA encryption
  • Key agreement protocols use ephemeral keys for forward secrecy
  • Random nonces and IVs are generated for each encryption operation

Basic Usage Examples:

// Generate keys using existing GoPKI infrastructure
rsaKeys, err := keypair.GenerateKeyPair[algo.KeySize, *algo.RSAKeyPair](2048)
if err != nil {
	log.Fatal(err)
}

// Simple data encryption
data := []byte("sensitive information")
encrypted, err := EncryptData(data, rsaKeys, DefaultEncryptOptions())
if err != nil {
	log.Fatal(err)
}

// Decrypt the data
decrypted, err := DecryptData(encrypted, rsaKeys, DefaultDecryptOptions())
if err != nil {
	log.Fatal(err)
}

// Large file encryption using envelope encryption
opts := DefaultEncryptOptions()
opts.UseEnvelopeEncryption = true
opts.Format = FormatRaw

largeData := make([]byte, 1024*1024) // 1MB data
encrypted, err = EncryptData(largeData, rsaKeys, opts)

Certificate-based Encryption:

// Load certificate from file
cert, err := cert.LoadCertificateFromFile("recipient.pem")
if err != nil {
	log.Fatal(err)
}

// Encrypt for certificate recipient
encrypted, err := EncryptForCertificate(data, cert, DefaultEncryptOptions())
if err != nil {
	log.Fatal(err)
}

Advanced Usage with Custom Options:

// Create custom encryption options
opts := EncryptOptions{
	Algorithm:             AlgorithmAuto,  // Auto-select based on key type
	UseEnvelopeEncryption: true,           // Use envelope encryption for large data
	Format:                FormatPKCS7,    // Use PKCS#7 output format
	KeyDerivationRounds:   100000,         // Custom KDF rounds
}

// Encrypt with custom options
encrypted, err := EncryptData(largeData, keyPair, opts)

Integration with Other GoPKI Packages:

This package is designed to work seamlessly with:
- keypair: For key generation and management
- cert: For certificate-based encryption workflows
- pkcs12: For importing/exporting encrypted key stores
- signing: For combined sign-then-encrypt workflows

Index ΒΆ

Constants ΒΆ

This section is empty.

Variables ΒΆ

View Source
var (
	ErrUnsupportedAlgorithm = errors.New("unsupported encryption algorithm")
	ErrUnsupportedFormat    = errors.New("unsupported encryption format")
	ErrInvalidKey           = errors.New("invalid encryption key")
	ErrDecryptionFailed     = errors.New("decryption failed")
	ErrDataTooLarge         = errors.New("data too large for encryption method")
	ErrInvalidRecipient     = errors.New("invalid recipient information")
	ErrExpiredData          = errors.New("encrypted data has expired")
	ErrInvalidFormat        = errors.New("invalid encrypted data format")
	ErrInvalidParameters    = errors.New("invalid encryption parameters")
)

Common error types

Functions ΒΆ

func EncodeData ΒΆ

func EncodeData(data *EncryptedData) ([]byte, error)

EncodeData encodes EncryptedData to CMS format bytes Since CMS is the only supported format, this is a convenience function

func ValidateCMS ΒΆ

func ValidateCMS(data CMS) error

ValidateCMS validates CMS format data using external library

func ValidateDecryptOptions ΒΆ

func ValidateDecryptOptions(opts DecryptOptions) error

ValidateDecryptOptions validates decryption options

func ValidateEncodedData ΒΆ

func ValidateEncodedData(data []byte) error

ValidateEncodedData validates that the data is in valid CMS format

func ValidateEncryptOptions ΒΆ

func ValidateEncryptOptions(opts EncryptOptions) error

ValidateEncryptOptions validates encryption options

Types ΒΆ

type Algorithm ΒΆ

type Algorithm string

Algorithm EncryptionAlgorithm represents the algorithm used for encryption

const (
	AlgorithmRSAOAEP  Algorithm = "RSA-OAEP"
	AlgorithmECDH     Algorithm = "ECDH"
	AlgorithmX25519   Algorithm = "X25519"
	AlgorithmAESGCM   Algorithm = "AES-GCM"
	AlgorithmEnvelope Algorithm = "Envelope"
)

AlgorithmRSAOAEP and related constants define supported encryption algorithms.

func GetAlgorithmForKeyType ΒΆ

func GetAlgorithmForKeyType(keyType string) Algorithm

GetAlgorithmForKeyType determines the appropriate encryption algorithm for a key type

type CMS ΒΆ

type CMS []byte

CMS represents CMS (Cryptographic Message Syntax) encoded data.

func EncodeToCMS ΒΆ

func EncodeToCMS(data *EncryptedData) (CMS, error)

EncodeToCMS converts EncryptedData to CMS format using external library

type CertificateEncryptor ΒΆ

type CertificateEncryptor interface {
	EncryptWithCertificate(data []byte, certificate *x509.Certificate, opts EncryptOptions) (*EncryptedData, error)
	SupportedAlgorithms() []Algorithm
}

CertificateEncryptor provides type-safe certificate-based encryption

type DecryptOptions ΒΆ

type DecryptOptions struct {
	// Expected algorithm (for validation)
	ExpectedAlgorithm Algorithm
	// Verify timestamp
	VerifyTimestamp bool
	// Maximum age for encrypted data
	MaxAge time.Duration
	// Time to verify certificate validity (default: now)
	VerifyTime time.Time
	// Skip expiration check
	SkipExpirationCheck bool
	// Additional validation options
	ValidationOptions map[string]any
	// Try OpenSSL format first during decryption
	// When true, attempts to decode as standard PKCS#7 EnvelopedData first
	// Falls back to GoPKI format if that fails
	// When false (default), auto-detects format based on structure
	TryOpenSSLFormat bool
}

DecryptOptions contains options for decryption operations

func DefaultDecryptOptions ΒΆ

func DefaultDecryptOptions() DecryptOptions

DefaultDecryptOptions returns default decryption options

type Decryptor ΒΆ

type Decryptor[K keypair.KeyPair] interface {
	Decrypt(encrypted *EncryptedData, keyPair K, opts DecryptOptions) ([]byte, error)
	SupportedAlgorithms() []Algorithm
}

Decryptor provides type-safe decryption operations

type EncryptOptions ΒΆ

type EncryptOptions struct {
	// Encryption algorithm to use
	Algorithm Algorithm
	// Output format
	Format Format
	// Include recipient certificate
	IncludeCertificate bool
	// Additional certificate recipients for multi-recipient encryption
	CertificateRecipients []*x509.Certificate
	// Key derivation function parameters
	KDF *KDFParams
	// Custom metadata
	Metadata map[string]any
	// OpenSSL compatibility mode (RSA only)
	// When true, uses standard PKCS#7 EnvelopedData format compatible with OpenSSL
	// When false (default), uses GoPKI's custom format supporting RSA/ECDSA/Ed25519
	// Note: Only works with RSA keys - ECDSA/Ed25519 will return error if this is true
	OpenSSLCompatible bool
}

EncryptOptions contains options for encryption operations

func DefaultEncryptOptions ΒΆ

func DefaultEncryptOptions() EncryptOptions

DefaultEncryptOptions returns default encryption options

type EncryptedData ΒΆ

type EncryptedData struct {
	// Algorithm used for encryption
	Algorithm Algorithm
	// Format of the encrypted data
	Format Format
	// The encrypted data bytes
	Data []byte
	// Encrypted symmetric key (for envelope encryption)
	EncryptedKey []byte
	// Initialization vector (for AES-GCM)
	IV []byte
	// Authentication tag (for AES-GCM)
	Tag []byte
	// Key derivation parameters (optional)
	KDF *KDFParams
	// Recipient information
	Recipients []*RecipientInfo
	// Timestamp when encrypted
	Timestamp time.Time
	// Additional metadata
	Metadata map[string]any
}

EncryptedData represents encrypted data with its metadata

func DecodeDataWithKey ΒΆ

func DecodeDataWithKey[T keypair.PrivateKey](data []byte, cert *x509.Certificate, privateKey T) (*EncryptedData, error)

DecodeDataWithKey decodes CMS format bytes back to EncryptedData using certificate and private key This is the secure way to decode CMS data that requires explicit decryption credentials.

T represents the private key type (*rsa.PrivateKey, *ecdsa.PrivateKey, or ed25519.PrivateKey)

func DecodeFromCMS ΒΆ

func DecodeFromCMS[T any](cmsData CMS, cert *x509.Certificate, privateKey T) (*EncryptedData, error)

DecodeFromCMS parses CMS format into EncryptedData using external library

Note: This function signature has changed from the original implementation. It now requires a certificate and private key for proper decryption, which is more secure and explicit.

The function is generic and accepts any private key type:

  • *rsa.PrivateKey for RSA keys
  • *ecdsa.PrivateKey for ECDSA keys
  • ed25519.PrivateKey for Ed25519 keys

This function auto-detects two formats:

  1. GoPKI format: JSON-wrapped envelope structure (supports RSA/ECDSA/Ed25519)
  2. OpenSSL format: Standard PKCS#7 EnvelopedData (RSA only)

Usage examples:

// Type inference (recommended)
data, err := DecodeFromCMS(cmsBytes, cert, rsaPrivateKey)

// Explicit type parameter
data, err := DecodeFromCMS[*rsa.PrivateKey](cmsBytes, cert, rsaPrivateKey)

type Encryptor ΒΆ

type Encryptor[K keypair.KeyPair] interface {
	Encrypt(data []byte, keyPair K, opts EncryptOptions) (*EncryptedData, error)
	SupportedAlgorithms() []Algorithm
}

Encryptor provides type-safe encryption operations

type Format ΒΆ

type Format string

Format EncryptionFormat represents the format of encrypted data Currently only CMS (RFC 5652) format is supported

const (
	FormatCMS Format = "cms" // RFC 5652 Cryptographic Message Syntax
)

FormatCMS defines the CMS output format for encrypted data.

type KDFParams ΒΆ

type KDFParams struct {
	// Algorithm (PBKDF2, scrypt, etc.)
	Algorithm string
	// Salt
	Salt []byte
	// Iterations (for PBKDF2)
	Iterations int
	// Key length
	KeyLength int
	// Additional parameters
	Params map[string]any
}

KDFParams contains key derivation function parameters

type MultiRecipientEncryptor ΒΆ

type MultiRecipientEncryptor interface {
	EncryptForCertificates(data []byte, certificates []*x509.Certificate, opts EncryptOptions) (*EncryptedData, error)
	AddCertificateRecipient(encrypted *EncryptedData, certificate *x509.Certificate) error
}

MultiRecipientEncryptor provides type-safe multi-recipient encryption for certificates

type PrivateKeyDecryptor ΒΆ

type PrivateKeyDecryptor[P keypair.PrivateKey] interface {
	DecryptWithPrivateKey(encrypted *EncryptedData, privateKey P, opts DecryptOptions) ([]byte, error)
	SupportedAlgorithms() []Algorithm
}

PrivateKeyDecryptor provides type-safe private key decryption

type PublicKeyEncryptor ΒΆ

type PublicKeyEncryptor[P keypair.PublicKey] interface {
	EncryptForPublicKey(data []byte, publicKey P, opts EncryptOptions) (*EncryptedData, error)
	SupportedAlgorithms() []Algorithm
}

PublicKeyEncryptor provides type-safe public key encryption

type RecipientInfo ΒΆ

type RecipientInfo struct {
	// Recipient's certificate (optional)
	Certificate *x509.Certificate
	// Key identifier
	KeyID []byte
	// Encrypted key for this recipient
	EncryptedKey []byte
	// Key encryption algorithm
	KeyEncryptionAlgorithm Algorithm
	// Additional fields for ECDSA/Ed25519 support
	// Ephemeral public key (for ECDH/X25519)
	EphemeralKey []byte
	// IV for key encryption (for AES-GCM)
	KeyIV []byte
	// Authentication tag for key encryption (for AES-GCM)
	KeyTag []byte
}

RecipientInfo contains information about an encryption recipient

Directories ΒΆ

Path Synopsis
Package asymmetric provides asymmetric encryption operations using RSA, ECDSA, and Ed25519 algorithms.
Package asymmetric provides asymmetric encryption operations using RSA, ECDSA, and Ed25519 algorithms.
Package certificate provides certificate-based encryption operations that integrate with the GoPKI certificate infrastructure for document-level encryption.
Package certificate provides certificate-based encryption operations that integrate with the GoPKI certificate infrastructure for document-level encryption.
Package envelope implements hybrid envelope encryption for efficient encryption of large data sets using a combination of symmetric and asymmetric cryptography.
Package envelope implements hybrid envelope encryption for efficient encryption of large data sets using a combination of symmetric and asymmetric cryptography.
Package symmetric provides AES-GCM symmetric encryption operations.
Package symmetric provides AES-GCM symmetric encryption operations.

Jump to

Keyboard shortcuts

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