AEAD¶
Authenticated Encryption with Associated Data (AEAD) provides confidentiality and integrity in a single operation. Every ciphertext carries an authentication tag – if anyone tampers with the data, decryption fails immediately with an exception rather than silently producing garbage.
Assumed imports
All examples assume the following:
import dev.whyoleg.cryptography.*
import dev.whyoleg.cryptography.algorithms.*
val provider = CryptographyProvider.Default
Basic Usage¶
Get the algorithm, generate a key, create a cipher, and encrypt/decrypt:
val aesGcm = provider.get(AES.GCM)
val key = aesGcm.keyGenerator().generateKey()
val cipher = key.cipher()
// Encrypt
val ciphertext = cipher.encrypt(plaintext = "secret message".encodeToByteArray())
// Decrypt
val plaintext = cipher.decrypt(ciphertext = ciphertext)
println(plaintext.decodeToString()) // secret message
Each call to encrypt generates a fresh random IV and prepends it to the output:
[IV | ciphertext | tag]. When you pass this to decrypt, the library splits it
automatically. The cipher object is reusable – call encrypt and decrypt on it as many times as needed.
Associated Data¶
Associated data (AAD) is authenticated but not encrypted. Use it to bind ciphertext to a context so it cannot be replayed elsewhere:
val cipher = key.cipher()
val userId = "user-123".encodeToByteArray()
// Encrypt with associated data
val ciphertext = cipher.encrypt(
plaintext = "secret".encodeToByteArray(),
associatedData = userId
)
// Decrypt -- must provide the same associated data
val plaintext = cipher.decrypt(
ciphertext = ciphertext,
associatedData = userId
)
If you provide different associated data at decryption – or omit it entirely – the authentication tag will not match and decryption throws an exception. The associated data itself is never included in the ciphertext output; both sides must know it independently.
Custom IV¶
By default, a random IV is generated and prepended to the ciphertext. If your protocol requires
a specific IV handling, use encryptWithIv
and decryptWithIv:
val cipher = key.cipher()
val iv = ByteArray(12) // 12 bytes for AES-GCM
val ciphertext = cipher.encryptWithIv(
iv = iv,
plaintext = "secret".encodeToByteArray()
)
// With custom IV, the output does NOT contain the IV -- only [ciphertext | tag]
val plaintext = cipher.decryptWithIv(
iv = iv,
ciphertext = ciphertext
)
Warning
Reusing an IV with the same key completely breaks AES-GCM security. Only use custom IVs when you have a reliable mechanism to guarantee uniqueness.
Streaming¶
For large data that does not fit in memory, use the kotlinx-io streaming API.
The cipher provides an ability to transform RawSource via
encryptingSource and decryptingSource
as well as RawSink with encryptingSink and decryptingSink:
val cipher = key.cipher()
val aad = "context".encodeToByteArray()
// Pull-based: wrap a source
val encryptedSource: RawSource = cipher.encryptingSource(plaintextSource, associatedData = aad)
val decryptedSource: RawSource = cipher.decryptingSource(ciphertextSource, associatedData = aad)
// Push-based: wrap a sink
val encryptingSink: RawSink = cipher.encryptingSink(destinationSink, associatedData = aad)
val decryptingSink: RawSink = cipher.decryptingSink(plaintextSink, associatedData = aad)
Custom IV variants are also available: encryptingSourceWithIv,
encryptingSinkWithIv, decryptingSourceWithIv,
decryptingSinkWithIv.
Supported Algorithms¶
| Algorithm | JDK | WebCrypto | Apple | CryptoKit | OpenSSL3 |
|---|---|---|---|---|---|
| AES-GCM |
|||||
| AES-CCM | |||||
| ChaCha20-Poly1305 |