Skip to content

goforj/crypt

Repository files navigation

crypt logo

Laravel-compatible symmetric encryption for Go - AES-128/256 CBC with HMAC, key rotation, and portable payloads.

Go Reference License: MIT Go Test Go version Latest tag Go Report Card

crypt mirrors Laravel's encryption format so Go services can read and write the same ciphertext as PHP apps. It signs every payload with an HMAC and supports graceful key rotation via APP_PREVIOUS_KEYS.

Features

  • 🔐 AES-128/256-CBC + HMAC-SHA256 payloads identical to Laravel
  • ♻️ Key rotation: decrypt falls back through APP_PREVIOUS_KEYS
  • 🔑 base64: key parsing (16- or 32-byte keys)
  • 🧪 Focused, table-driven tests for tampering, rotation, and key sizes
  • 📦 Zero dependencies beyond the Go standard library

Install

go get github.com/goforj/crypt

Quickstart

package main

import (
	"fmt"
	"os"

	"github.com/goforj/crypt"
)

func main() {
	// Typical Laravel-style key: base64 + 32 bytes (AES-256) or 16 bytes (AES-128).
	if err := os.Setenv("APP_KEY", "base64:..."); err != nil {
		panic(err)
	}

	ciphertext, err := crypt.Encrypt("secret")
	if err != nil {
		panic(err)
	}

	plaintext, err := crypt.Decrypt(ciphertext)
	if err != nil {
		panic(err)
	}

	fmt.Println(plaintext) // "secret"
}

Key format and rotation

  • APP_KEY must be prefixed with base64: and decode to 16 bytes (AES-128) or 32 bytes (AES-256).
  • APP_PREVIOUS_KEYS is optional; provide a comma-delimited list of older keys (same format).
    Decrypt will try the current key first, then each previous key until one succeeds.
  • Encrypt always uses the current APP_KEY; no auto re-encrypt is performed on decrypt.

Example:

export APP_KEY="base64:J63qRTDLub5NuZvP+kb8YIorGS6qFYHKVo6u7179stY="
export APP_PREVIOUS_KEYS="base64:2nLsGFGzyoae2ax3EF2Lyq/hH6QghBGLIq5uL+Gp8/w="

CLI helpers

Generate a Laravel-style key:

k, _ := crypt.GenerateAppKey()
fmt.Println(k) // base64:...

Parse an existing key string:

keyBytes, err := crypt.ReadAppKey("base64:...") // len == 16 or 32

Runnable examples

Every function has a corresponding runnable example under ./examples.

These examples are generated directly from the documentation blocks of each function, ensuring the docs and code never drift. These are the same examples you see here in the README and GoDoc.

An automated test executes every example to verify it builds and runs successfully.

This guarantees all examples are valid, up-to-date, and remain functional as the API evolves.

API Index

Group Functions
Encryption Decrypt Encrypt
Key management GenerateAppKey GenerateKeyToEnv GetAppKey GetPreviousAppKeys ReadAppKey RotateKeyInEnv

Encryption

Decrypt · readonly

Decrypt decrypts an encrypted payload using the APP_KEY from environment. Falls back to APP_PREVIOUS_KEYS when the current key cannot decrypt.

Example: decrypt using current key

keyStr, _ := crypt.GenerateAppKey()
_ = os.Setenv("APP_KEY", keyStr)
c, _ := crypt.Encrypt("secret")
p, _ := crypt.Decrypt(c)
godump.Dump(p)

// #string "secret"

Example: decrypt ciphertext encrypted with a previous key

oldKeyStr, _ := crypt.GenerateAppKey()
newKeyStr, _ := crypt.GenerateAppKey()
_ = os.Setenv("APP_KEY", oldKeyStr)
oldCipher, _ := crypt.Encrypt("rotated")
_ = os.Setenv("APP_KEY", newKeyStr)
_ = os.Setenv("APP_PREVIOUS_KEYS", oldKeyStr)
plain, err := crypt.Decrypt(oldCipher)
godump.Dump(plain, err)

// #string "rotated"
// #error <nil>

Encrypt · readonly

Encrypt encrypts a plaintext using the APP_KEY from environment.

keyStr, _ := crypt.GenerateAppKey()
_ = os.Setenv("APP_KEY", keyStr)
ciphertext, err := crypt.Encrypt("secret")
godump.Dump(err == nil, ciphertext != "")

// #bool true
// #bool true

Key management

GenerateAppKey · readonly

GenerateAppKey generates a random base64 app key prefixed with "base64:".

key, _ := crypt.GenerateAppKey()
godump.Dump(key)

// #string "base64:..."

GenerateKeyToEnv · mutates-filesystem

GenerateKeyToEnv mimics Laravel's key:generate. It generates a new APP_KEY and writes it to the provided .env path. Other keys are preserved; APP_KEY is replaced/added.

tmp := filepath.Join(os.TempDir(), ".env")
key, err := crypt.GenerateKeyToEnv(tmp)
godump.Dump(err, key)

// #error <nil>
// #string "base64:..."

GetAppKey · readonly

GetAppKey retrieves the APP_KEY from the environment and parses it.

keyStr, _ := crypt.GenerateAppKey()
_ = os.Setenv("APP_KEY", keyStr)
key, err := crypt.GetAppKey()
godump.Dump(len(key), err)

// #int 32
// #error <nil>

GetPreviousAppKeys · readonly

GetPreviousAppKeys retrieves and parses APP_PREVIOUS_KEYS from the environment. Keys are expected to be comma-delimited and prefixed with "base64:".

k1, _ := crypt.GenerateAppKey()
k2, _ := crypt.GenerateAppKey()
_ = os.Setenv("APP_PREVIOUS_KEYS", k1+", "+k2)
keys, err := crypt.GetPreviousAppKeys()
godump.Dump(len(keys), err)

// #int 2
// #error <nil>

ReadAppKey · readonly

ReadAppKey parses a base64 encoded app key with "base64:" prefix. Accepts 16-byte keys (AES-128) or 32-byte keys (AES-256) after decoding.

key128raw := make([]byte, 16)
_, _ = rand.Read(key128raw)
key128str := "base64:" + base64.StdEncoding.EncodeToString(key128raw)

key256str, _ := crypt.GenerateAppKey()

key128, _ := crypt.ReadAppKey(key128str)
key256, _ := crypt.ReadAppKey(key256str)
godump.Dump(len(key128), len(key256))

// #int 16
// #int 32

RotateKeyInEnv · mutates-filesystem

RotateKeyInEnv mimics Laravel's key:rotate. It moves the current APP_KEY into APP_PREVIOUS_KEYS (prepended) and writes a new APP_KEY.

tmp := filepath.Join(os.TempDir(), ".env")
oldKey, _ := crypt.GenerateAppKey()
_ = os.WriteFile(tmp, []byte("APP_KEY="+oldKey+"\n"), 0o644)
newKey, err := crypt.RotateKeyInEnv(tmp)
godump.Dump(err == nil, newKey != "")

// #bool true
// #bool true

About

Opinionated, environment-aware encryption for Go, inspired by Laravel’s Crypt facade.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages