Randomized AEAD (AES-256-GCM)
The aead package — AES-256-GCM with a self-generated nonce prepended to every ciphertext, plus the exact key size, tag size, and wire layout.
github.com/sonr-io/crypto/aead is a thin, opinionated wrapper over the standard library’s crypto/cipher.NewGCM. It exists to remove the two decisions people get wrong with raw AES-GCM: it fixes the key size at AES-256 and it generates and transports the nonce for you. The package doc comment cites NIST SP 800-38D.
There is exactly one type, AESGCMCipher, and one constructor. There is no AES-128 mode, no key-unwrapping helper, no streaming interface, and no algorithm identifier on the wire.
When to use it
Reach for aead whenever you have a 32-byte symmetric key and some bytes to protect: session payloads, encrypted records, wrapped blobs, anything where you want confidentiality plus integrity and you can afford a fresh random nonce per message.
Do not use it when:
- You need the ciphertext to be a stable function of the plaintext (e.g. an encrypted database column you still have to query by equality). Use
daedinstead. - You will encrypt an enormous number of messages under a single key. A random 96-bit nonce is subject to the birthday bound, so nonce collisions become non-negligible after roughly 2^32 messages; rotate keys long before that.
- You need to encrypt a stream too large to hold in memory.
EncryptandDecryptare one-shot over full slices.
Constants
All three constants are plain int literals in aes_gcm.go.
| Constant | Value | Meaning |
|---|---|---|
aead.NonceSize |
12 |
96-bit GCM nonce — the size GCM is fastest with, and the only size accepted |
aead.TagSize |
16 |
128-bit GCM authentication tag |
aead.KeySize |
32 |
AES-256 key size, and the only accepted key length |
Ciphertext layout
This is the single most important fact about the package:
Encrypt / EncryptWithNonce output:
┌────────────────┬──────────────────────────┬──────────────────┐
│ nonce 12 bytes │ ciphertext len(plaintext)│ GCM tag 16 bytes │
└────────────────┴──────────────────────────┴──────────────────┘
└── produced by gcm.Seal(nil, nonce, pt, aad) ┘
Encrypt generates the nonce itself from crypto/rand and prepends it to the sealed output. So:
len(output) == NonceSize + len(plaintext) + TagSize— the test asserts exactly this.Decryptexpects that same layout. It slicesdata[:12]as the nonce and passesdata[12:](ciphertext and tag) togcm.Open. You never manage nonces yourself, and you never store them separately.- The minimum valid ciphertext is
NonceSize + TagSize= 28 bytes (an empty plaintext). Shorter input returnsinvalid ciphertext length: minimum 28 bytes requiredbefore any crypto runs.
The AAD is not part of the output. Whatever you pass as aad must be reproducible at decrypt time from context you already have — a record ID, a version tag, a tenant name.
Usage
Grounded in aead/aes_gcm_test.go (TestAESGCMEncryptDecrypt):
package main
import (
"crypto/rand"
"fmt"
"github.com/sonr-io/crypto/aead"
)
func main() {
// AES-256 key: exactly aead.KeySize bytes.
key := make([]byte, aead.KeySize)
if _, err := rand.Read(key); err != nil {
panic(err)
}
cipher, err := aead.NewAESGCM(key)
if err != nil {
panic(err)
}
plaintext := []byte("secret data")
aad := []byte("additional auth data") // authenticated, not encrypted, not stored
// Encrypt returns nonce || ciphertext || tag.
ct, err := cipher.Encrypt(plaintext, aad)
if err != nil {
panic(err)
}
fmt.Println(len(ct) == aead.NonceSize+len(plaintext)+aead.TagSize) // true
// Decrypt takes that whole blob back, plus the identical AAD.
pt, err := cipher.Decrypt(ct, aad)
if err != nil {
panic(err) // "decryption and authentication failed: ..."
}
fmt.Printf("%s\n", pt) // secret data
}
One AESGCMCipher value can be reused for many messages — the underlying cipher.AEAD is stateless and safe for concurrent use, and each Encrypt draws a fresh nonce.
API reference
NewAESGCM(key []byte)(*AESGCMCipher, error)
Constructor. Errors unless len(key) == 32. Wraps aes.NewCipher then cipher.NewGCM.
(*AESGCMCipher, error)Encrypt(plaintext, aad []byte)([]byte, error)
Draws a fresh 12-byte nonce from crypto/rand, seals, and returns nonce || ciphertext || tag. Accepts empty plaintext and nil aad.
([]byte, error)Decrypt(data, aad []byte)([]byte, error)
Expects nonce || ciphertext || tag. Errors if len(data) < 28, or if the tag or AAD does not verify.
([]byte, error)EncryptWithNonce(plaintext, aad, nonce []byte)?([]byte, error)
Same output layout, but with a caller-supplied nonce. Errors unless len(nonce) == 12. See the danger note below.
([]byte, error)GetNonceSize()?int
Always returns the NonceSize constant, 12.
intGetTagSize()?int
Always returns the TagSize constant, 16.
intAESGCMCipher has exactly one field, an unexported gcm cipher.AEAD. There is nothing to configure and nothing to inspect.