{"id":51436482,"url":"https://github.com/ubgo/crypt","last_synced_at":"2026-07-05T07:02:09.443Z","repository":{"id":355582833,"uuid":"1226756658","full_name":"ubgo/crypt","owner":"ubgo","description":"Batteries-included Go cryptography library: AES-256-GCM \u0026 ChaCha20-Poly1305 authenticated encryption, argon2id/bcrypt password hashing, HMAC \u0026 Ed25519 signing, HKDF, key rotation, KMS envelope encryption, and secure random — safe defaults, stdlib-only, byte-for-byte interop with the TypeScript crypt-ts.","archived":false,"fork":false,"pushed_at":"2026-07-03T08:51:44.000Z","size":235,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-03T09:27:54.637Z","etag":null,"topics":["aead","aes-gcm","argon2","bcrypt","chacha20-poly1305","cryptography","ed25519","encryption","envelope-encryption","go","golang","hkdf","hmac","key-rotation","password-hashing","security"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/ubgo/crypt","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ubgo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-01T19:52:25.000Z","updated_at":"2026-07-03T09:13:23.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ubgo/crypt","commit_stats":null,"previous_names":["ubgo/crypt"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/ubgo/crypt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ubgo%2Fcrypt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ubgo%2Fcrypt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ubgo%2Fcrypt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ubgo%2Fcrypt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ubgo","download_url":"https://codeload.github.com/ubgo/crypt/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ubgo%2Fcrypt/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35145900,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-05T02:00:06.290Z","response_time":100,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aead","aes-gcm","argon2","bcrypt","chacha20-poly1305","cryptography","ed25519","encryption","envelope-encryption","go","golang","hkdf","hmac","key-rotation","password-hashing","security"],"created_at":"2026-07-05T07:02:07.392Z","updated_at":"2026-07-05T07:02:09.377Z","avatar_url":"https://github.com/ubgo.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# crypt\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/ubgo/crypt.svg)](https://pkg.go.dev/github.com/ubgo/crypt) [![Go Report Card](https://goreportcard.com/badge/github.com/ubgo/crypt)](https://goreportcard.com/report/github.com/ubgo/crypt) [![test](https://github.com/ubgo/crypt/actions/workflows/test.yml/badge.svg)](https://github.com/ubgo/crypt/actions/workflows/test.yml) [![lint](https://github.com/ubgo/crypt/actions/workflows/lint.yml/badge.svg)](https://github.com/ubgo/crypt/actions/workflows/lint.yml) ![coverage](https://img.shields.io/badge/coverage-95%25-brightgreen) [![tag](https://img.shields.io/github/v/tag/ubgo/crypt?sort=semver)](https://github.com/ubgo/crypt/tags) [![license](https://img.shields.io/badge/license-Apache%202.0-blue)](./LICENSE) ![Go](https://img.shields.io/badge/go-1.25-00ADD8?logo=go)\n\n\u003e Authenticated encryption, password hashing, webhook signing, and secure random — wrapped around the Go standard library with safe defaults and byte-for-byte interop with the TypeScript counterpart [`crypt-ts`](https://github.com/ubgo/crypt-ts).\n\n**crypt** is a batteries-included **Go cryptography library**: AES-256-GCM and ChaCha20-Poly1305 authenticated encryption (AEAD), argon2id and bcrypt password hashing, HMAC and Ed25519 signing, HKDF key derivation, graceful key rotation, KMS envelope encryption, streaming file encryption, and a CSPRNG-backed secure-random toolkit — all with safe defaults, a versioned wire format, and byte-for-byte interoperability with Node.js via `crypt-ts`.\n\n```go\nimport \"github.com/ubgo/crypt\"\n\nkey, _ := crypt.RandomBytes(crypt.AEADKeySize)\nct, _ := crypt.Seal(key, []byte(\"hello, world\"), nil)\npt, _ := crypt.Open(key, ct, nil)\n// pt == []byte(\"hello, world\")\n```\n\nThat's the whole API for the most common case. Need more? Read on.\n\n## Contents\n\n- [Is this for you?](#is-this-for-you)\n- [30-second tour](#30-second-tour)\n- [Why crypt?](#why-crypt)\n- [What's included](#whats-included)\n- [API at a glance](#api-at-a-glance)\n- [Binding \u0026 rotating keys — design notes](#binding--rotating-keys--design-notes)\n- [FAQ](#faq)\n- [Documentation](#documentation)\n- [TypeScript counterpart](#typescript-counterpart)\n- [Install](#install)\n- [Status](#status)\n- [Reporting vulnerabilities](#reporting-vulnerabilities)\n- [License](#license)\n\n## Is this for you?\n\n`crypt` is built for Go applications that need a curated set of cryptography primitives done well, with safe defaults, no foot-guns, and optionally a Node.js sibling that produces byte-identical output. **Reach for it when you're about to write any of the following:**\n\n- Encrypt a database column at rest (API client secrets, PII, encryption keys, webhook secrets) and decrypt it back later.\n- Sign outgoing webhooks with HMAC (or Ed25519 public-key) and verify incoming ones — Stripe-style with timestamp tolerance, etc.\n- Hash user passwords correctly with modern parameters (argon2id, or bcrypt for compatibility).\n- Generate cryptographically-random API keys, magic-link tokens, CSRF tokens, session IDs.\n- Issue stateless time-locked tokens (password reset, email verify, magic login) with embedded expiry.\n- Decrypt in Node.js what a Go service encrypted (or vice versa) — same wire format on both sides.\n- Compare an API key in constant time without leaking timing.\n- Interoperate with an existing AES-CBC system, or read ciphertext you already wrote in CBC.\n- Encrypt large files in chunks (`SealStream` / `OpenStream`) with per-chunk authentication and truncation detection.\n- Derive per-tenant or per-purpose sub-keys from a single master with HKDF.\n- Rotate keys gracefully — `KeyRing` with embedded kid; old data still readable, new writes use the active key.\n- Use ChaCha20-Poly1305 instead of AES-GCM (no AES-NI hardware, or defense-in-depth diversity).\n- Encrypt to a recipient's public key — X25519 + ChaCha20-Poly1305 (sealed-box), age-style.\n- Sign with Ed25519 — public-key signatures where verifiers don't share the signing key.\n- Envelope encryption with a KMS — per-row DEK wrapped under a KMS-managed KEK.\n\nIf any of those are on your plate, this is the package.\n\n**Not for you if:** you need TLS, PKI, X.509, JWT/JOSE, certificate management, browser/WebCrypto, or post-quantum crypto. Use the std library or a specialized package for those.\n\n## 30-second tour\n\n### Encrypt a database column at rest\n\n```go\nsealer, _ := crypt.NewSealer(appKey) // 32 bytes from secrets manager\n\nenc, _ := sealer.Seal([]byte(secret), nil)\ndb.Exec(`UPDATE partners SET client_secret = $1 WHERE id = $2`, enc, id)\n\nplain, _ := sealer.Open(row.ClientSecret, nil)\n```\n\n### Bind a token to a user (so it can't be replayed for another)\n\n```go\nct, _ := sealer.Seal(payload, []byte(\"user:\"+userID))\n\npt, err := sealer.Open(ct, []byte(\"user:\"+userID))\n// err == ErrTampered if userID differs from issue time\n```\n\n### Sign and verify a webhook\n\n```go\nmac := crypt.Sign(secret, body)              // signer\nok := crypt.Verify(secret, body, mac)        // verifier (constant-time)\n```\n\n### Hash and verify a password\n\n```go\nhash, _ := crypt.HashPassword(plaintext)\nok, _   := crypt.VerifyPassword(plaintext, hash)\n```\n\n### Generate an API token\n\n```go\ntoken, _ := crypt.RandomToken(32) // 43-char URL-safe string\n```\n\n### Cross-language: Go encrypts, Node decrypts\n\n```go\n// Go side\nct, _ := crypt.Seal(sharedKey, payload, nil)\nreturn ct\n```\n\n```ts\n// Node side, using @ubgo/crypt\nimport { open } from \"@ubgo/crypt\"\nconst plaintext = open(sharedKey, ct)\n```\n\nSame wire format, byte-for-byte. Verified by shared test vectors in CI.\n\n## Why crypt?\n\nEvery Go service ends up reinventing the same five wrappers around `crypto/aes`, `crypto/hmac`, `crypto/rand`, `crypto/subtle`, and `golang.org/x/crypto/argon2`. Each reinvention gets some part wrong:\n\n- AES-CBC instead of GCM (no authentication).\n- A \"default\" key string committed to source.\n- `bytes.Equal` for HMAC verification (timing leak).\n- Argon2 with hand-tuned parameters that drift from OWASP recommendations.\n- A wire format that the Node sibling decodes wrong for any plaintext \u003e 16 bytes.\n\nThe last one is not theoretical: a hand-rolled wrapper in a Node.js codebase shipped silent corruption for any plaintext over 16 bytes before this package existed. `crypt` is one wrapper covering both languages, with shared test vectors enforcing wire-format parity in CI so divergence is caught at PR review rather than in production.\n\n| | **crypt** | Go stdlib (DIY) | Hand-rolled wrappers |\n|---|---|---|---|\n| Authenticated encryption by default | ✅ AEAD out of the box | ⚠️ you must choose GCM and wire the nonce | ⚠️ often CBC without a MAC |\n| Versioned wire format (future algos, safe upgrades) | ✅ | ❌ | ❌ |\n| Cross-language (Go ↔ Node) byte parity | ✅ shared vectors in CI | ❌ | ⚠️ hand-matched, drifts |\n| Key rotation with a grace window | ✅ `KeyRing` | ❌ | ❌ |\n| Envelope encryption / KMS | ✅ `EnvelopeSealer` | ❌ | ⚠️ |\n| Password hashing (argon2id + bcrypt) | ✅ OWASP params, PHC output | ⚠️ tune it yourself | ⚠️ |\n| Constant-time compare wired in | ✅ | ⚠️ easy to forget `subtle` | ❌ |\n| Third-party dependencies | ✅ stdlib + `x/crypto` only | ✅ | ⚠️ varies |\n\nIf you want to assemble the primitives yourself, the standard library is right there and it's excellent. If you want the *correct assembly* — authenticated, versioned, rotatable, and readable from Node — that's `crypt`.\n\n## What's included\n\n**Authenticated encryption (AES-256-GCM)** — `Seal`, `Open`, `Sealer`. Modern AEAD with a versioned wire format so future algorithms slot in without breaking decrypt of old data.\n\n**Password hashing (argon2id)** — `HashPassword`, `VerifyPassword`. OWASP-recommended parameters; PHC string output so future re-tunes are backward-compatible.\n\n**HMAC signing** — `Sign`, `Verify`. Constant-time verification.\n\n**Secure random** — `RandomBytes`, `RandomToken` (URL-safe base64), `RandomHex`. OS CSPRNG.\n\n**Constant-time compare** — `ConstantTimeEqual`. Wraps `crypto/subtle`.\n\n**AES-CBC** — `EncryptCBC`, `DecryptCBC` (16/24/32-byte keys for AES-128/192/256). First-class peer of AES-GCM — use it when interop with an existing AES-CBC system is required (PHP/Java/Python), or when reading ciphertext your application already wrote in this format. CBC has no built-in authentication; layer HMAC on top (encrypt-then-MAC) or use `Seal/Open` if you need tamper detection. A `crypt.OpenAuto` helper auto-detects AEAD vs CBC for migration scripts.\n\n**Cross-language wire format** — every AEAD and HMAC output is byte-identical to the TypeScript counterpart, validated by `testdata/vectors.json` consumed by both repos' tests.\n\nFor the full feature catalog with use cases, see the [long-form guide](./USAGE.md) and the [27 runnable examples](./examples).\n\n## API at a glance\n\n```go\n// AEAD\nfunc Seal(key, plaintext, aad []byte) (string, error)\nfunc Open(key []byte, ciphertext string, aad []byte) ([]byte, error)\n\ntype Sealer struct { /* ... */ }\nfunc NewSealer(key []byte) (*Sealer, error)\nfunc (s *Sealer) Seal(plaintext, aad []byte) (string, error)\nfunc (s *Sealer) Open(ciphertext string, aad []byte) ([]byte, error)\n\n// Random\nfunc RandomBytes(n int) ([]byte, error)\nfunc RandomToken(n int) (string, error)   // URL-safe base64-no-pad\nfunc RandomHex(n int) (string, error)\n\n// Signing\nfunc Sign(key, data []byte) []byte\nfunc Verify(key, data, mac []byte) bool\nfunc ConstantTimeEqual(a, b []byte) bool\n\n// Password\nfunc HashPassword(plaintext string) (string, error)\nfunc VerifyPassword(plaintext, hash string) (bool, error)\n\n// AES-CBC (16/24/32-byte keys; no built-in auth — pair with HMAC if needed)\nfunc EncryptCBC(key []byte, plaintext []byte) (string, error)\nfunc DecryptCBC(key []byte, ciphertext string) ([]byte, error)\nfunc OpenAuto(key []byte, ciphertext string, aad []byte) ([]byte, error)\n\n// ChaCha20-Poly1305 (alternative AEAD; wire version 0x02)\nfunc SealChaCha20(key, plaintext, aad []byte) (string, error)\nfunc OpenChaCha20(key []byte, ciphertext string, aad []byte) ([]byte, error)\n\n// Bcrypt password hashing (compatibility with bcrypt-using systems)\nfunc HashPasswordBcrypt(plaintext string, cost int) (string, error)\nfunc VerifyPasswordBcrypt(plaintext, hash string) (bool, error)\n\n// HKDF key derivation\nfunc DeriveKey(masterKey, salt, info []byte, length int) ([]byte, error)\n\n// KeyRing for rotation (wire version 0x03 with embedded kid)\ntype KeyRing struct { /* ... */ }\nfunc NewKeyRing(activeKid string, activeKey []byte) (*KeyRing, error)\nfunc (r *KeyRing) Add(kid string, key []byte) error\nfunc (r *KeyRing) Remove(kid string) error\nfunc (r *KeyRing) SetActive(kid string) error\nfunc (r *KeyRing) ActiveKid() string\nfunc (r *KeyRing) Seal(plaintext, aad []byte) (string, error)\nfunc (r *KeyRing) Open(ciphertext string, aad []byte) ([]byte, error)\n\n// Time-locked tokens (embedded expiry; ErrExpired sentinel)\nfunc IssueToken(key, payload []byte, ttl time.Duration, aad []byte) (string, error)\nfunc VerifyToken(key []byte, token string, aad []byte) ([]byte, error)\n\n// Streaming AEAD (chunked file encryption with truncation detection)\nfunc SealStream(key []byte, r io.Reader, w io.Writer, chunkSize int) error\nfunc OpenStream(key []byte, r io.Reader, w io.Writer) error\n\n// Ed25519 public-key signatures\nfunc GenerateEd25519() (publicKey ed25519.PublicKey, privateKey ed25519.PrivateKey, err error)\nfunc SignEd25519(priv ed25519.PrivateKey, data []byte) ([]byte, error)\nfunc VerifyEd25519(pub ed25519.PublicKey, data, sig []byte) (bool, error)\n\n// X25519 + ChaCha20-Poly1305 sealed-box (asymmetric encrypt; wire version 0x05)\nfunc GenerateKeyPair() (publicKey, privateKey []byte, err error)\nfunc SealAsymmetric(recipientPublicKey, plaintext []byte) (string, error)\nfunc OpenAsymmetric(recipientPrivateKey []byte, ciphertext string) ([]byte, error)\n\n// Envelope encryption (KMS-wrapped DEK; wire version 0x06)\ntype KMS interface { /* ... */ }\ntype StaticKMS struct { /* ... */ }       // in-memory adapter for tests/dev\nfunc NewStaticKMS() *StaticKMS\ntype EnvelopeSealer struct { /* ... */ }\nfunc NewEnvelopeSealer(kms KMS, keyID string) *EnvelopeSealer\n```\n\nFull reference at [pkg.go.dev](https://pkg.go.dev/github.com/ubgo/crypt).\n\n## Binding \u0026 rotating keys — design notes\n\nA few questions come up often enough to answer here, since the choices are deliberate.\n\n**\"Can I set one global encrypt key instead of passing it everywhere?\"** Use a `Sealer`, not a global. `NewSealer(key)` binds the key once at startup; downstream code then calls `sealer.Seal(pt, aad)` / `sealer.Open(ct, aad)` with no key argument. That gives you the \"set once, call bare\" ergonomics without a package-level default. There is intentionally no `SetDefaultKey` / `SealDefault`: a global would be hidden mutable state — hard to test (parallel tests share it), impossible to run two keys at once, and a silent zero-key footgun if used before it's set. Prefer storing the `Sealer` on your app/DI struct over hand-rolling `encryptToken(key, ...)` wrapper functions.\n\n**\"Can a Sealer update its key on the fly?\"** No — a `Sealer` is immutable by design and has no `Rekey` method. The key is bound at `NewSealer` and never changes, which is exactly what makes a `Sealer` safe to share across goroutines with no lock. A mutable key would force a mutex or atomic load on every `Seal`/`Open` to serve a swap that happens rarely, and a hard swap would instantly make all previously-sealed ciphertext undecryptable.\n\n**\"Then how do I rotate keys?\"** Pick by need:\n\n- **`KeyRing`** — restart-free rotation with a grace window. Writes use the active key; reads dispatch by the `kid` embedded in each ciphertext, so old data stays readable while it migrates. This is the right tool for compliance rotation and compromise response.\n- **Swap the whole `Sealer`** at a safe boundary (e.g. config reload) behind `atomic.Pointer[Sealer]`. Cheap and race-free, but single-key only — old ciphertext isn't readable after the swap unless you keep the old `Sealer` around. Note: reassigning a plain `*Sealer` variable while other goroutines are calling it is a data race; use `atomic.Pointer`.\n\n## FAQ\n\n**Is crypt safe to use in production?** It's a thin, auditable set of wrappers over Go's standard library and `golang.org/x/crypto` — no custom primitives — with authenticated encryption by default, ~95% test coverage, and a shared known-answer vector file that enforces cross-language wire-format parity in CI. Review the [threat model](./SECURITY.md) for exactly what is and isn't defended.\n\n**Does crypt roll its own cryptography?** No. Encryption is `crypto/cipher` AES-GCM and `golang.org/x/crypto/chacha20poly1305`; hashing is `argon2` / `bcrypt`; signatures are `crypto/ed25519`; key agreement is `curve25519`. crypt only supplies safe defaults, a versioned wire format, and ergonomics — never a novel algorithm.\n\n**Should I use AES-GCM or ChaCha20-Poly1305?** Default to AES-256-GCM (`Seal`/`Open`) — it's fastest on any CPU with AES-NI, which is nearly all server hardware. Reach for ChaCha20-Poly1305 (`SealChaCha20`) on hardware without AES-NI (some ARM/embedded) or when you want algorithm diversity for defense-in-depth. Both share the same versioned format.\n\n**How do I rotate encryption keys without downtime?** Use `KeyRing`: new writes use the active key, and reads dispatch by the key id embedded in each ciphertext, so previously-encrypted data stays readable throughout the migration. See [Binding \u0026 rotating keys](#binding--rotating-keys--design-notes).\n\n**Can Node.js decrypt what a Go service encrypted?** Yes. The [`crypt-ts`](https://github.com/ubgo/crypt-ts) package produces and consumes the exact same bytes; both repos run against the same `testdata/vectors.json` so any divergence fails CI.\n\n**How is this different from just using `crypto/aes` myself?** The standard library gives you the primitives; crypt gives you the correct assembly — authentication wired in, a versioned nonce/header layout, constant-time verification, KDFs with vetted parameters, key rotation, and Node interoperability — so the parts you'd otherwise get subtly wrong are already right.\n\n**Does crypt support public-key (asymmetric) encryption?** Yes — `SealAsymmetric` / `OpenAsymmetric` implement an age-style X25519 + ChaCha20-Poly1305 sealed box, and `SignEd25519` / `VerifyEd25519` provide Ed25519 signatures for verifiers that don't hold the signing key.\n\n**Is AES-CBC deprecated?** No — it's a first-class peer kept for interop with existing AES-CBC systems and for reading ciphertext you already wrote. It has no built-in authentication, so pair it with HMAC (encrypt-then-MAC) or use `Seal`/`Open` when you need tamper detection.\n\n**What Go version does crypt need?** Go 1.25 or later; the only dependency is `golang.org/x/crypto`.\n\n## Documentation\n\n- **[USAGE.md](./USAGE.md)** — long-form guide, every common pattern explained\n- **[RECIPES.md](./RECIPES.md)** — short copy-pasteable snippets, organized by task\n- **[examples/](./examples)** — 27 runnable end-to-end programs (sessions, magic links, CSRF, audit logs, encrypted files, key rotation, multi-tenant, ...)\n- **[SECURITY.md](./SECURITY.md)** — threat model, what's defended, what isn't\n- **[WIRE_FORMAT.md](./WIRE_FORMAT.md)** — byte-by-byte ciphertext spec for cross-language interop\n- **[MIGRATION.md](./MIGRATION.md)** — moving from the legacy string-typed AES-CBC wrappers to AES-GCM\n- **[BENCHMARKS.md](./BENCHMARKS.md)** — real numbers and what they mean\n- **[FAQ.md](./FAQ.md)** — answers to questions you'll have\n- **[CHANGELOG.md](./CHANGELOG.md)**\n\n## TypeScript counterpart\n\n[`crypt-ts`](https://github.com/ubgo/crypt-ts) — same API surface (minus password hashing — server-side only), same wire format, byte-for-byte interoperable.\n\n```ts\nimport { seal, open } from \"@ubgo/crypt\"\nconst ct = seal(sharedKey, \"hello\")\nconst pt = open(sharedKey, ct).toString(\"utf8\") // identical to Go\n```\n\n## Install\n\n```sh\ngo get github.com/ubgo/crypt\n```\n\nRequires Go 1.25 or later.\n\n## Status\n\n**Current release: `v0.2.0`.** The entire surface documented above is implemented, tested (~95% coverage), and covered by cross-language known-answer vectors: AEAD (AES-256-GCM and ChaCha20-Poly1305), argon2id/bcrypt password hashing, HMAC and Ed25519 signing, HKDF, `KeyRing` rotation, KMS envelope encryption, streaming AEAD, time-locked tokens, and secure random.\n\nPre-1.0, so: the **wire format is stable** and pinned by the shared vectors (ciphertext written today stays readable), but the Go API may still see small refinements before `v1.0.0`. Pin a version and check the [CHANGELOG](./CHANGELOG.md) before upgrading. See [`MIGRATION.md`](./MIGRATION.md) for moving off the legacy string-typed AES-CBC wrappers.\n\n## Reporting vulnerabilities\n\nOpen a private security advisory: https://github.com/ubgo/crypt/security/advisories/new\n\nWe aim to acknowledge within 48 hours and patch P0 issues within 7 days.\n\n## License\n\n[Apache License 2.0](./LICENSE)\n\n\u003csub\u003ecrypt — a Go cryptography library for authenticated encryption (AES-256-GCM, ChaCha20-Poly1305 / AEAD), password hashing (argon2id, bcrypt), HMAC and Ed25519 signing, HKDF key derivation, key rotation, KMS envelope encryption, streaming file encryption, and secure random tokens. Apache-2.0, standard-library-only, with a byte-for-byte TypeScript / Node.js counterpart (crypt-ts).\u003c/sub\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fubgo%2Fcrypt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fubgo%2Fcrypt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fubgo%2Fcrypt/lists"}