Symmetric & Secrets
Bulk encryption, password-based key derivation, and the secret-hygiene helpers — how to pick between them and how they compose.
This is the “boring” half of the library, and the half you will actually touch on every request path. Nothing here is generic over the elliptic-curve abstraction: every package on these pages takes and returns []byte. That makes the layer easy to reason about and easy to misuse, so each page is explicit about the exact key sizes, ciphertext layouts, and constants involved.
The packages split into three jobs:
- Encrypt bytes.
aeadis randomized AES-256-GCM — the default choice.daedis AES-SIV, a deterministic AEAD for the narrow cases where you need the same plaintext to produce the same ciphertext. - Turn a secret into a key.
argon2stretches a human password into key material and produces PHC-encoded password hashes.subtleis the HKDF + X25519 layer for turning an already-high-entropy secret (a shared secret, a master key) into per-purpose subkeys. - Handle the secret carefully.
secure,salt,password, andsubtle/randomare small hygiene helpers: zeroization, constant-time comparison, salt generation and storage, password policy checks, and raw randomness.
Pick one
| Your goal | Package | Entry point |
|---|---|---|
| Encrypt a payload, blob, or message | aead |
NewAESGCM(key) → Encrypt(pt, aad) |
| Encrypt a lookup key or identifier you must still be able to search by | daed |
NewAESSIV(key) → EncryptDeterministically |
| Wrap key material with no nonce to manage | daed |
NewAESSIV(key) |
| Derive an encryption key from a user’s password | argon2 |
New(DefaultConfig()).DeriveKey(pw, salt) |
| Store a verifiable password hash | argon2 |
HashPassword / VerifyPassword |
| Split one master secret into several purpose-bound subkeys | subtle |
ComputeHKDF("SHA256", key, salt, info, 32) |
| Agree on a key with a remote peer | subtle |
X25519 trio → ComputeHKDF |
| Generate a salt | salt or argon2 |
salt.GenerateDefault() / kdf.GenerateSalt() |
| Generate raw random bytes | secure |
SecureRandom(buf) |
| Compare two secrets without leaking timing | argon2 |
CompareHashes(a, b) |
| Zero a key out of memory after use | secure |
Zeroize(key) |
| Enforce a password policy at signup | password |
NewValidator(nil).Validate(pw) |
How they compose
The realistic pipeline is: get entropy, stretch or expand it into a 32-byte key, encrypt with that key, then zero the key.
package vault
import (
"github.com/sonr-io/crypto/aead"
"github.com/sonr-io/crypto/argon2"
"github.com/sonr-io/crypto/secure"
)
func sealWithPassword(password, plaintext, aad []byte) (key, salt, ct []byte, err error) {
kdf := argon2.New(argon2.DefaultConfig()) // Argon2id, 64 MiB, t=1, p=4
salt, err = kdf.GenerateSalt() // 32 bytes
if err != nil {
return nil, nil, nil, err
}
key = kdf.DeriveKey(password, salt) // 32 bytes == aead.KeySize
defer secure.Zeroize(key)
cipher, err := aead.NewAESGCM(key)
if err != nil {
return nil, nil, nil, err
}
ct, err = cipher.Encrypt(plaintext, aad) // nonce || ciphertext || tag
return key, salt, ct, err
}
Two things make that snippet work, and both are worth internalizing:
argon2.DefaultConfig().KeyLengthis32, andaead.KeySizeis32. The KDF output plugs straight into the AEAD constructor with no truncation or padding.aead.Encryptgenerates its own nonce and prepends it, so the only thing you have to persist alongside the ciphertext is the salt.
Sizes at a glance
Every one of these is a compile-time constant in the package named, not a default you can override. Getting a size wrong is a constructor error, never a silent truncation.
| Constant | Value | Where |
|---|---|---|
aead.KeySize |
32 |
AES-256-GCM key — the only length accepted |
aead.NonceSize |
12 |
GCM nonce, generated and prepended by Encrypt |
aead.TagSize |
16 |
GCM authentication tag |
daed.AESSIVKeySize |
64 |
AES-SIV double-length key: 32-byte CMAC key ‖ 32-byte CTR key |
salt.DefaultSaltSize |
32 |
Recommended salt size |
salt.MinSaltSize / salt.MaxSaltSize |
16 / 1024 |
Hard bounds enforced by salt.Generate |
An aead ciphertext is therefore always len(plaintext) + 28 bytes; an AES-SIV ciphertext is always len(plaintext) + 16. Neither carries a version byte or key identifier, so any framing you need is yours to add.
What stays your responsibility
These packages cover the primitive, not the protocol. Everything below is out of scope for this layer and has to live in your application:
- Key lifetime and rotation. Nothing tracks how many messages a key has protected, and nothing versions a key.
aeadwill happily encrypt forever under one key. - Salt and hash persistence.
salt.SaltStoreis an in-memory map with no mutex;argon2.HashPasswordis the only helper that packages a salt into something durable. - Replay and freshness. AEAD authenticity says “this ciphertext was produced by someone holding the key”, never “recently” or “once”. Bind timestamps or counters into the AAD.
- Rate limiting on password paths.
argon2makes each guess expensive; it does not make guessing impossible. - Unicode normalization of anything a human types, before both validation and derivation.
What this layer does not do
There is no key storage here — no keystore, no envelope format, no versioned header. aead.Encrypt hands you nonce || ciphertext || tag and nothing else; if you need an algorithm identifier or a key ID on the wire, you frame it yourself. Likewise there is no ChaCha20-Poly1305, no AES-128, and no streaming/chunked API: aead is AES-256-GCM one-shot only.
Next
Randomized AEAD
AES-256-GCM. The default for encrypting anything. Exact key size, nonce handling, and ciphertext layout.
Deterministic AEAD
AES-SIV with a 64-byte key. Nonce-free and misuse-resistant, at the cost of leaking plaintext equality.
Key derivation
Argon2id presets with exact parameters, PHC hash encoding, HKDF, and the X25519 ECDH trio.
Secret hygiene
Zeroization, salts and the salt store, password policy, randomness — and what each one really guarantees.
Everything outside this layer — signatures, threshold protocols, zero-knowledge proofs — is generic over the curve abstraction described in Foundations: curves.