Add OpenSSL::Cipher#gcm_tag and #gcm_tag= for AEAD ciphers#17096
Open
paulocoghi wants to merge 5 commits into
Open
Add OpenSSL::Cipher#gcm_tag and #gcm_tag= for AEAD ciphers#17096paulocoghi wants to merge 5 commits into
OpenSSL::Cipher#gcm_tag and #gcm_tag= for AEAD ciphers#17096paulocoghi wants to merge 5 commits into
Conversation
Adds support for retrieving and supplying GCM authentication tags, enabling authenticated cipher modes such as AES-256-GCM. - `#gcm_tag` retrieves the 16-byte tag after encryption (post `#final`) - `#gcm_tag=` supplies the tag before decryption (pre `#final`) Binds `EVP_CIPHER_CTX_ctrl` and the `EVP_CTRL_GCM_GET_TAG` / `EVP_CTRL_GCM_SET_TAG` constants in `LibCrypto`.
Co-authored-by: Johannes Müller <straightshoota@gmail.com>
Sija
reviewed
Jun 23, 2026
Co-authored-by: Sijawusz Pur Rahnama <sija@sija.pl>
Sija
reviewed
Jun 24, 2026
straight-shoota
approved these changes
Jun 25, 2026
ysbaddaden
approved these changes
Jun 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation and context:
I'm implementing a pure Crystal SWIM protocol (Scalable Weakly-consistent Infection-style Process Group Membership) at github.com/alumna/crystal-swim
I tried to use GCM (
AES-256-GCM) provided by OpenSSL from the Crystal's OpenSSL module, to implement payload encryption based on AES-GCM here.But as you can see in the code, I needed to reopen
OpenSSL::Cipherand bind the missingEVP_CIPHER_CTX_ctrlC function.Please inform me in case there is interest in upstream such fix, which is what this PR does. But contrary to my temporary fix in my shard, this PR reuses and respects the existing logic in Crystal's
OpenSSL::Ciphermodule.I also added a proper spec test by extending
spec/std/openssl/cipher_spec.cr, which is fully passing with:$ ./bin/crystal spec spec/std/openssl/cipher_spec.cr ... Finished in 1.27 milliseconds 3 examples, 0 failures, 0 errors, 0 pendingWhat this PR brings: GCM authentication tag support to
OpenSSL::CipherThis PR adds
#gcm_tagand#gcm_tag=methods toOpenSSL::Cipher, enabling the use of authenticated cipher modes such as AES-256-GCM.Opening the door for AEAD support
Beyond GCM, this PR lays groundwork for broader AEAD support.
Binding
EVP_CIPHER_CTX_ctrl(OpenSSL's general cipher control interface) makes it easier to extendOpenSSL::Cipherin the future with features such as:Additionally, the existing
#authenticated?method previously had no actionable counterpart, and#gcm_tagand#gcm_tag=complete that API surface, making authenticated cipher modes fully usable without resorting to rawLibCryptocalls.Extra Background
GCM (Galois/Counter Mode) is an AEAD (Authenticated Encryption with Associated Data) mode of operation.
Unlike a plain cipher mode such as CBC, GCM simultaneously encrypts the data and produces a short authentication tag over the ciphertext. During decryption, the tag is verified before the plaintext is returned. Thus, if the ciphertext was tampered with,
#finalraises anOpenSSL::Cipher::Error. This makes GCM the standard choice for secure network protocols and storage formats.Crystal's
OpenSSL::Cipheralready exposes#authenticated?to detect AEAD ciphers, but provided no way to actually retrieve or supply the authentication tag, making AES-GCM (and other AEAD modes) unusable in practice.Changes
[tl;dr]
src/openssl/lib_crypto.cr: Adds theEVP_CTRL_GCM_GET_TAGandEVP_CTRL_GCM_SET_TAGconstants, and binds theEVP_CIPHER_CTX_ctrlC function.src/openssl/cipher.cr: Adds two methods:#gcm_tag : Bytes: retrieves the 16-byte authentication tag after encryption (must be called after#final).#gcm_tag=(tag : Bytes) : Bytes: supplies the authentication tag before decryption (must be called before#final).Both methods raise
OpenSSL::Cipher::Errorif called on a non-authenticated cipher.spec/std/openssl/cipher_spec.cr: Adds a round-trip encrypt/decrypt spec for AES-256-GCM.[detailed]
1.
EVP_CIPHER_CTX_ctrlis a general-purpose door, not a GCM-only oneThe C function
EVP_CIPHER_CTX_ctrlis OpenSSL's generic "send a control command to a cipher context" function. GCM tags are just one of many things it can do. Future contributors could use the same binding to, for example, change the IV length for GCM, set tag lengths other than 16 bytes, or support other AEAD modes like ChaCha20-Poly1305 (which uses the sameEVP_CTRL_AEAD_GET_TAG/EVP_CTRL_AEAD_SET_TAGconstants, which are aliases for the same values 0x10/0x11).2.
authenticated?seemed a dead-end and this PR tried to complete itThe
authenticated?method already existed inOpenSSL::Cipher, letting the code to detect whether a cipher is an AEAD mode. But there was nothing we could actually do with that information. You cannot retrieve or supply the tag in the current shape. This PR makesauthenticated?meaningful by providing the corresponding tag API.3.
EVP_GCM_TLS_TAG_LENwas already defined but unused in the public APIThe constant
EVP_GCM_TLS_TAG_LEN = 16was already present inlib_crypto.cr(used internally for TLS), but no public method referenced it. The newgcm_tagmethod uses it as the tag size, making the constant earn its place.