With version 26.5 of the MBS Xojo Plugins, the CipherMBS class gets two new methods: ProcessStringAAD and ProcessMemoryAAD. They let you pass Additional Authenticated Data (AAD) to an AES-GCM encryption. Let us look at what this is, why you may want it and how to use it in your Xojo projects.
A quick recap: AES-GCM
GCM (Galois/Counter Mode) is an authenticated encryption mode. Besides encrypting your data, it calculates a 16 byte tag over the result. When you decrypt, you pass this tag back in, and the decryption only succeeds if nothing was changed. A flipped bit in the encrypted data, a wrong key or a wrong tag, and the decryption fails instead of handing you garbage.
CipherMBS supports GCM since a long time with the aes_128_gcm, aes_192_gcm and aes_256_gcm ciphers (and the ARIA variants). You query the tag with GetGCMTag after encrypting and set it with SetGCMTag before finalizing the decryption.
What is Additional Authenticated Data?
Often you have data that does not need to be secret, but must not be tampered with. Think of:
- a header with a version number or the text encoding,
- a record ID or user ID the encrypted value belongs to,
- a file name, a timestamp or a protocol message header.
This data stays readable, e.g. so you can route a message or look up a record before decrypting. But you want to be sure nobody changed it. With AAD you feed this data into the GCM calculation. It is not encrypted and not part of the output, but it goes into the tag. To decrypt, you must provide exactly the same AAD again. If someone changed a single byte of it, the decryption fails.
A typical use case: you store encrypted values in a database and use the record ID as AAD. Now an attacker can’t copy the encrypted value from one record into another one, since the decryption would fail with the other record ID.
The new methods
ProcessStringAAD(data as String) as Boolean
ProcessMemoryAAD(data as MemoryBlock) as Boolean
Both return true on success. Call them after EncryptInit or DecryptInit and before you pass the actual data to ProcessString or ProcessMemory. You may call them several times to add more AAD.
Example
Here is a full round trip. We encrypt a text with a header as AAD, then decrypt it again with the same header:
Var key As String = "mysecretkey"
Var data As String = "Hello World. Just a test"
Var AAD As String = "encoding=UTF-8;"
// encrypt
Var c As CipherMBS = CipherMBS.aes_128_gcm
Call c.EncryptInit key
Call c.ProcessStringAAD(AAD)
Var Encrypted As String = c.ProcessString(data) + c.FinalizeAsString
Var GCMTag As MemoryBlock = c.GetGCMTag
// decrypt
c = CipherMBS.aes_128_gcm
Call c.DecryptInit key
c.SetGCMTag GCMTag
Call c.ProcessStringAAD(AAD)
Var Decrypted As String = c.ProcessString(Encrypted) + c.FinalizeAsString
Break // inspect in debugger
If the tag, the AAD or the encrypted data doesn’t match, the CipherMBS class raises an UnsupportedOperationException in ProcessString, so you never get unverified data back.
Please note that the AAD is passed in the same order as on encryption: first the AAD, then the encrypted data. The encrypted output has the same length as the input, as GCM works like a stream cipher. Nothing of the AAD ends up in there.
To store or transmit the result, you keep three pieces together: the AAD in plain text, the encrypted data and the tag. Since encrypted data and tag are binary, use EncodeBase64 or EncodeHex before you put them into a text field.
Doing it right: key and IV
The example above is short to show the concept. For real use, please follow two rules:
Use a proper key. AES-128 needs a 16 byte key, AES-256 a 32 byte key. Instead of padding a password with zeros, hash it, e.g. with SHA-256 for AES-256. Even better, use a proper key derivation function with salt for passwords.
Never reuse an IV with the same key. If you don’t pass an IV, it is filled with zeros. For GCM, using the same key and IV twice is fatal: an attacker can learn about your data and even forge tags. So generate a new random IV for every encryption and store it with the result. The IV doesn’t need to be secret. For GCM, the IV is normally 12 bytes, which the IVLength property reports.
Here is a more complete example with AES-256, a random IV and a record ID as AAD:
Var password As String = "mysecretkey"
Var key As MemoryBlock = Crypto.SHA2_256(password) // 32 bytes for AES-256
Var data As String = "Credit limit: 5000 EUR"
Var AAD As String = "customer=12345;"
// encrypt
Var c As CipherMBS = CipherMBS.aes_256_gcm
Var IV As MemoryBlock = Crypto.GenerateRandomBytes(c.IVLength)
Call c.EncryptInit key, IV
Call c.ProcessStringAAD(AAD)
Var Encrypted As String = c.ProcessString(data) + c.FinalizeAsString
Var Tag As MemoryBlock = c.GetGCMTag
// store IV + tag + encrypted data, AAD is known from the record
Var stored As String = EncodeBase64(IV + Tag + Encrypted, 0)
// decrypt
Var raw As String = DecodeBase64(stored)
Var IV2 As MemoryBlock = raw.LeftBytes(12)
Var Tag2 As MemoryBlock = raw.MiddleBytes(12, 16)
Var Encrypted2 As String = raw.MiddleBytes(28)
Var d As CipherMBS = CipherMBS.aes_256_gcm
Call d.DecryptInit key, IV2
d.SetGCMTag Tag2
Call d.ProcessStringAAD(AAD)
Try
Var Decrypted As String = d.ProcessString(Encrypted2) + d.FinalizeAsString
MessageBox Decrypted
Catch err As UnsupportedOperationException
MessageBox "Decryption failed: data, tag or AAD was modified."
End Try
If you now change the AAD for the decryption to “customer=12346;”, you get the UnsupportedOperationException, even if key, IV, tag and encrypted data are all correct.
Compatibility
Since CipherMBS uses OpenSSL, the result is standard AES-GCM. You can exchange data with other platforms and languages, e.g. with CryptoKit on Apple platforms, the Web Crypto API in JavaScript (the additionalData parameter), Java’s Cipher.updateAAD() or .NET’s AesGcm class. Just make sure all sides agree on key, IV, tag length and of course the AAD bytes. Watch the text encoding: “Müller” in UTF-8 has different bytes than in Windows ANSI!
Conclusion
Additional Authenticated Data is a small addition with a big benefit: you bind your encrypted data to its context. Headers, IDs and metadata stay readable, but can’t be changed or mixed up without being noticed.
The new methods are included in version 26.5 of the MBS Xojo Encryption Plugin and work on macOS, Windows and Linux. Please try them and let us know if you have questions.
This article was drafted by AI from notes and edited by a human.