aes_gcm
AES-GCM Crystal Shard
A Crystal shard providing AES-256-GCM (Galois/Counter Mode) authenticated encryption and decryption.
Features
- ✅ AES-256-GCM encryption and decryption
- ✅ Authentication tag support (protects against tampering)
- ✅ Sequel column encryption support - Decrypt Ruby sequel-column-encryption data
- ✅ Configurable IV and tag sizes
- ✅ Base64 encoding/decoding helpers
- ✅ Direct bindings to OpenSSL for full GCM support
- ✅ Type-safe API with Crystal's type system
- ✅ 29 comprehensive specs
Why This Shard?
Crystal's standard OpenSSL::Cipher library doesn't expose methods to get/set authentication tags for GCM mode. This shard provides direct bindings to OpenSSL's EVP_CIPHER_CTX_ctrl function to enable full GCM functionality.
Installation
Add this to your application's shard.yml:
dependencies:
aes_gcm:
path: ./shard
Then run:
shards install
Usage
Sequel Column Encryption (Quick Start)
If you're working with the Sequel column_encryption plugin in Ruby, you can decrypt data with one line:
require "aes_gcm"
key = "e74aaac7a0b7f97159bb787dd593a3cb" # Your 32-byte (256-bit) encryption key
encrypted = "AAAAAGqIftnXqY76h5bbGh4pnmN8tNsfzGfw4sJcvWkmnRV46FuG762BI0vjoC7quADZ48Su-NfQiHoYHXwuV3v8rPlsM69rREkYReE11m9Y" # Base64 encoded encrypted data
# Simple decryption
plaintext = AesGcm::SequelColumnEncryption.decrypt(encrypted, key)
puts plaintext # => "Hello, World!"
# Decryption with metadata
info = AesGcm::SequelColumnEncryption.decrypt_with_info(encrypted, key)
puts info[:plaintext] # => "Hello, World!"
puts info[:format] # => "not_searchable"
puts info[:searchable] # => false
# Validate format
if AesGcm::SequelColumnEncryption.valid_format?(encrypted)
puts "Valid Sequel encrypted data"
end
Basic Encryption/Decryption
require "aes_gcm"
cipher = AesGcm::Cipher.new
key = "e74aaac7a0b7f97159bb787dd593a3cb"
plaintext = "Hello, World!"
# Encrypt
encrypted = cipher.encrypt(
key: key,
plaintext: plaintext
)
# Access encrypted components
puts encrypted.iv.hexstring # Initialization vector
puts encrypted.auth_tag.hexstring # Authentication tag
puts encrypted.ciphertext.hexstring # Encrypted data
# Decrypt
decrypted = cipher.decrypt(encrypted)
puts String.new(decrypted) # "Hello, World!"
Base64 Encoding
require "aes_gcm"
cipher = AesGcm::Cipher.new
key = "e74aaac7a0b7f97159bb787dd593a3cb"
# Encrypt and encode to base64
encoded = cipher.encrypt_base64(
key: key,
plaintext: "secret message"
)
# Decrypt from base64
decoded = cipher.decrypt_base64(
encoded: encoded,
key: key
)
puts String.new(decoded)
Custom Configuration
# Custom IV and tag sizes
cipher = AesGcm::Cipher.new(
iv_size: 12, # 96-bit IV (default)
tag_size: 16, # 128-bit tag (default)
key_size: 32 # 256-bit key (default)
)
API Documentation
AesGcm::SequelColumnEncryption
Module for decrypting data encrypted with Ruby's sequel-column-encryption gem.
Methods
-
decrypt(data_base64 : String, key : String | Bytes, remove_padding : Bool = true) : String- Decrypts Sequel column encryption format data
data_base64: Base64-encoded encrypted datakey: Encryption key (32 bytes for the master key)remove_padding: Remove Sequel padding (default: true)- Returns decrypted plaintext as String
- Raises
DecryptionErrorif decryption fails
-
decrypt_with_info(data_base64 : String, key : String | Bytes) : NamedTuple- Decrypts and returns metadata about the encryption format
- Returns:
{plaintext, flags, key_id, searchable, format} - Example:
info = AesGcm::SequelColumnEncryption.decrypt_with_info(encrypted, key) info[:plaintext] # => "John Doe" info[:format] # => "not_searchable" info[:searchable] # => false
-
valid_format?(data_base64 : String) : Bool- Checks if data appears to be in Sequel encryption format
- Does not attempt decryption, only validates structure
- Returns true if data has valid Sequel format
AesGcm::Cipher
Main cipher class for AES-256-GCM operations.
Methods
-
encrypt(key, plaintext, iv = nil) : EncryptedData- Encrypts plaintext and returns encrypted data with IV and auth tag
key: 32-byte encryption key (String or Bytes)plaintext: Data to encrypt (String or Bytes)iv: Optional IV (defaults to random)
-
decrypt(key, ciphertext, iv, auth_tag) : Bytes- Decrypts ciphertext and verifies authenticity
- Raises
OpenSSL::Cipher::Errorif authentication fails
-
decrypt(encrypted : EncryptedData) : Bytes- Convenience method to decrypt from EncryptedData struct
-
encrypt_base64(key, plaintext) : String- Encrypts and returns URL-safe base64 encoded string
-
decrypt_base64(encoded, key) : Bytes- Decrypts from URL-safe base64 encoded string
AesGcm::EncryptedData
Struct containing all components needed for decryption.
Properties
ciphertext : Bytes- Encrypted dataiv : Bytes- Initialization vectorauth_tag : Bytes- Authentication tagkey : Bytes- Encryption key
Methods
to_base64 : String- Encode all components to URL-safe base64self.from_base64(encoded, key, iv_size = 12, tag_size = 16) : EncryptedData
Security Notes
-
Key Management: Never hardcode encryption keys in your source code. Use environment variables or secure key management systems.
-
Authentication: GCM mode provides authenticated encryption. If decryption fails with a
OpenSSL::Cipher::Error, it means:- The data was tampered with
- Wrong encryption key was used
- Data is corrupted
-
IV Reuse: Never reuse the same IV with the same key. This implementation generates random IVs by default.
-
Key Size: This shard uses AES-256 (32-byte keys). Ensure your keys have sufficient entropy.
Examples
See the examples/ directory for more examples:
basic_usage.cr- Basic encryption/decryption examplessequel_column_encryption.cr- Decrypting Sequel column encryption format
Run examples:
cd shard
crystal run examples/basic_usage.cr
crystal run examples/sequel_column_encryption.cr
Technical Details
This shard uses direct C bindings to OpenSSL's EVP_CIPHER_CTX_ctrl function to access GCM-specific operations:
EVP_CTRL_GCM_SET_TAG(0x11) - Set authentication tag for decryptionEVP_CTRL_GCM_GET_TAG(0x10) - Get authentication tag after encryptionEVP_CTRL_GCM_SET_IVLEN(0x9) - Set custom IV length
Note on Linking: We don't use @[Link("crypto")] in our C bindings because Crystal's OpenSSL module already links to libcrypto. Adding it would cause duplicate library warnings on macOS and other platforms.
Limitations
-
AAD is not supported: Additional Authenticated Data (AAD) is not supported.
-
Algorithm: Only AES-256-GCM is supported. Other GCM variants (AES-128-GCM, AES-192-GCM) could be added in future versions.
Troubleshooting
"ld: warning: ignoring duplicate libraries: '-lcrypto'" on macOS
This warning has been fixed in version 0.1.0. If you see this warning in older versions, it's because Crystal's OpenSSL module already links to libcrypto. The warning is harmless but can be fixed by removing the @[Link("crypto")] annotation.
Decryption fails with "authentication verification failed"
This error means:
- Wrong encryption key
- Data has been tampered with
- Corrupted ciphertext or auth tag
"Key must be 32 bytes" error
AES-256 requires exactly 32 bytes (256 bits) for the key. Ensure your key string is exactly 32 bytes:
key = "12345678901234567890123456789012" # Exactly 32 bytes
puts key.bytesize # Should print 32
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT License
Credits
Inspired by the need to decrypt Sequel column encryption data in Crystal.
aes_gcm
- 0
- 0
- 0
- 0
- 0
- about 14 hours ago
- November 9, 2025
MIT License
Fri, 06 Mar 2026 20:02:28 GMT