crypt

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 16, 2025 License: MIT Imports: 14 Imported by: 0

README

crypt logo

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

Go Reference 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.

Example: encrypt with current APP_KEY

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:".

Example: generate an AES-256 key

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.

Example: generate and write APP_KEY to a temp .env

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.

Example: read APP_KEY and ensure the correct size

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:".

Example: parse two previous keys (mixed AES-128/256)

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.

Example: parse AES-128 and AES-256 keys

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.

Example: rotate APP_KEY and prepend old key to APP_PREVIOUS_KEYS

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

Behavior parity with Laravel

  • AES-CBC with PKCS#7 padding
  • HMAC-SHA256 over IV + ciphertext
  • JSON payload wrapped in base64 (fields: iv, value, mac)
  • Compatible with Laravel's Crypt::encryptString / decryptString

Testing

go test ./...

License

MIT

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Decrypt

func Decrypt(encodedPayload string) (string, error)

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

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>

func Encrypt

func Encrypt(plaintext string) (string, error)

Encrypt encrypts a plaintext using the APP_KEY from environment. @group Encryption @behavior readonly

Example: encrypt with current APP_KEY

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

// #bool true
// #bool true

func GenerateAppKey

func GenerateAppKey() (string, error)

GenerateAppKey generates a random base64 app key prefixed with "base64:". @group Key management @behavior readonly

Example: generate an AES-256 key

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

// #string "base64:..."

func GenerateKeyToEnv

func GenerateKeyToEnv(envPath string) (string, error)

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. @group Key management @behavior mutates-filesystem

Example: generate and write APP_KEY to a temp .env

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

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

func GetAppKey

func GetAppKey() ([]byte, error)

GetAppKey retrieves the APP_KEY from the environment and parses it. @group Key management @behavior readonly

Example: read APP_KEY and ensure the correct size

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

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

func GetPreviousAppKeys

func GetPreviousAppKeys() ([][]byte, error)

GetPreviousAppKeys retrieves and parses APP_PREVIOUS_KEYS from the environment. Keys are expected to be comma-delimited and prefixed with "base64:". @group Key management @behavior readonly

Example: parse two previous keys (mixed AES-128/256)

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>

func ReadAppKey

func ReadAppKey(key string) ([]byte, error)

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

Example: parse AES-128 and AES-256 keys

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

func RotateKeyInEnv

func RotateKeyInEnv(envPath string) (string, error)

RotateKeyInEnv mimics Laravel's key:rotate. It moves the current APP_KEY into APP_PREVIOUS_KEYS (prepended) and writes a new APP_KEY. @group Key management @behavior mutates-filesystem

Example: rotate APP_KEY and prepend old key to APP_PREVIOUS_KEYS

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

Types

type EncryptedPayload

type EncryptedPayload struct {
	IV    string `json:"iv"`
	Value string `json:"value"`
	MAC   string `json:"mac"`
}

EncryptedPayload is the JSON structure wrapped in base64 used for ciphertext.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL