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 / AES-256 encryption compatible with Laravel
  • Authenticated encryption (AES-CBC + HMAC)
  • Transparent key rotation via APP_PREVIOUS_KEYS
  • Zero dependencies (stdlib only)
  • Deterministic, testable API
  • Safe defaults with explicit failure modes

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"
}

Here’s a cleaner, tighter rewrite that keeps the same meaning but reads more confidently and fluently:

Key format & rotation

crypt follows Laravel’s key format and rotation model.

  • APP_KEY must be prefixed with base64: and decode to either 16 bytes (AES-128) or 32 bytes (AES-256).
  • APP_PREVIOUS_KEYS is optional and may contain a comma-separated list of older keys in the same format.
  • During decryption, the current key is tried first, followed by any previous keys.
  • Encryption always uses the current APP_KEY; previous keys are never used for encryption.

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