Skip to content

Encryption & Hashing

λ Cosmos provides encryption and hashing implementations through the contract.Encrypter and contract.Hasher interfaces.

type Encrypter interface {
Encrypt(value []byte) ([]byte, error)
Decrypt(value []byte) ([]byte, error)
}
type Hasher interface {
Hash(value []byte) ([]byte, error)
Check(value []byte, hash []byte) (bool, error)
}

Both implementations use authenticated encryption (AEAD), which provides both confidentiality and integrity. The nonce is automatically generated and prepended to the ciphertext.

import "github.com/studiolambda/cosmos/framework/crypto"
// Key must be 16, 24, or 32 bytes for AES-128, AES-192, or AES-256
key := []byte("your-32-byte-secret-key-here!!!!") // 32 bytes = AES-256
enc, err := crypto.NewAES(key)
if err != nil {
log.Fatal(err)
}
// Encrypt
ciphertext, err := enc.Encrypt([]byte("sensitive data"))
// Decrypt
plaintext, err := enc.Decrypt(ciphertext)

AES-GCM is the standard choice for most applications. Use AES-256 (32-byte key) for maximum security.

Bind ciphertext to a specific context to prevent cross-context misuse:

enc, err := crypto.NewAES(key)
enc.AdditionalData = []byte("user:42")
ciphertext, err := enc.Encrypt([]byte("sensitive data"))
// Decryption will fail if AdditionalData doesn't match

Zero encryption key material from memory when done:

enc, err := crypto.NewAES(key)
defer enc.Close() // zeros key from memory
ciphertext, err := enc.Encrypt(plaintext)
import "github.com/studiolambda/cosmos/framework/crypto"
// Key must be exactly 32 bytes
key := []byte("your-32-byte-secret-key-here!!!!")
enc, err := crypto.NewChaCha20(key)
if err != nil {
log.Fatal(err)
}
ciphertext, err := enc.Encrypt([]byte("sensitive data"))
plaintext, err := enc.Decrypt(ciphertext)

ChaCha20-Poly1305 is an alternative to AES-GCM that performs better on hardware without AES instruction set support (e.g., older ARM devices). On modern x86 hardware with AES-NI, performance is comparable.

ChaCha20 also supports AdditionalData and Close() with the same API as AES-GCM.

AES-GCMChaCha20-Poly1305
Key size16, 24, or 32 bytes32 bytes only
Hardware accelerationAES-NI on x86Software-optimized
Best forServers with AES-NIMobile, embedded, or mixed hardware
StandardNISTIETF RFC 8439

Both are secure and widely used. AES-256-GCM is the most common choice for server applications.

The recommended choice for password hashing:

import "github.com/studiolambda/cosmos/framework/hash"
hasher := hash.NewArgon2()
// Hash a password
hashed, err := hasher.Hash([]byte("user-password"))
// Verify a password
ok, err := hasher.Check([]byte("user-password"), hashed)
if ok {
// password matches
}

Customize the Argon2 parameters:

hasher := hash.NewArgon2With(hash.Argon2Config{
// Configure memory, iterations, parallelism, etc.
})

The default configuration uses argon2.DefaultConfig() which provides secure defaults suitable for most applications.

A well-established alternative:

import "github.com/studiolambda/cosmos/framework/hash"
hasher := hash.NewBcrypt()
hashed, err := hasher.Hash([]byte("user-password"))
ok, err := hasher.Check([]byte("user-password"), hashed)

Customize the cost factor:

hasher := hash.NewBcryptWith(hash.BcryptOptions{
Cost: 12, // default is 12 (OWASP recommendation)
})

Higher cost means slower hashing, which is more resistant to brute force attacks but increases CPU usage during authentication. The default cost of 12 follows OWASP recommendations.

Argon2Bcrypt
Memory-hardYesNo
GPU resistanceHighModerate
ConfigurableMemory, time, parallelismCost factor only
RecommendedYes (modern standard)Yes (proven, widely supported)

Argon2 is the winner of the Password Hashing Competition and is recommended for new applications. Bcrypt remains a solid choice and is well-supported across ecosystems if you need interoperability.

Inject encryption or hashing instances into the request context:

app.Use(middleware.Provide(encKey, encrypter))
app.Use(middleware.Provide(hashKey, hasher))
func register(w http.ResponseWriter, r *http.Request) error {
h := r.Context().Value(hashKey).(contract.Hasher)
body, err := request.JSON[RegisterRequest](r)
if err != nil {
return err
}
hashed, err := h.Hash([]byte(body.Password))
if err != nil {
return err
}
// Store hashed password...
}

Remember to zero key material when your application shuts down:

defer enc.Close() // zeros key from memory