Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

credvault

A small, dependency-light Go library for storing credentials and secrets at rest — password hashing, envelope encryption, and context-bound authenticated data (AAD) that stops an attacker with database access from relocating one row's ciphertext into another.

Three focused packages:

Package Purpose
pwhash Argon2id password hashing + verification (constant-time, tunable cost).
cryptoenv AES-256-GCM envelope encryption under a single master key — Encrypt/Decrypt and the AAD-bound EncryptCtx/DecryptCtx.
cryptoctx The Additional Authenticated Data (AAD) bound to each encrypted column, centralized so the encrypt and decrypt sites can never drift.

Extracted and generalized from a production system where every stored secret (OAuth tokens, passwords) was encrypted at rest and bound to its owner.

The threat it defends against

Encrypting a column with a single master key stops an attacker who can read the database. It does not stop an attacker who can write it: with plain AES-GCM they can copy account A's encrypted password into account B's row, and it still decrypts — now they can log in as B with A's known password. Binding the ciphertext to a stable per-row identity via AAD closes that hole: the AAD won't match on Open, so the relocated blob fails to decrypt.

cryptoctx centralizes those bindings so the encrypt site and the decrypt site always compute the same AAD:

aad := cryptoctx.AccessToken(accountID)          // "cred:v1:access_token_enc:<id>"
blob, _ := cipher.EncryptCtx(plaintext, aad)     // bound to this account

// A DB-write attacker who moves `blob` into another account's row:
_, err := cipher.DecryptCtx(blob, cryptoctx.AccessToken(otherID))
// err != nil — the AAD no longer matches.

Two binding strengths are provided: per-row (AccessToken, RefreshToken, AccountPassword — includes a stable row id/login) and domain-tag (OAuthTxn — a per-purpose constant, used where a clean row key isn't available at every site).

Usage

import (
    "github.com/biglill/credvault/pwhash"
    "github.com/biglill/credvault/cryptoenv"
    "github.com/biglill/credvault/cryptoctx"
)

// --- Passwords -------------------------------------------------------------
hash, _ := pwhash.Hash("correct horse battery staple")
ok := pwhash.Verify(hash, "correct horse battery staple")   // true, constant-time

// --- Secrets at rest -------------------------------------------------------
cipher, _ := cryptoenv.New(masterKey)                       // 32-byte key from env/KMS
blob, _  := cipher.EncryptCtx(token, cryptoctx.RefreshToken(accountID))
token, _ := cipher.DecryptCtx(blob, cryptoctx.RefreshToken(accountID))

Backward-compatible decryption

DecryptCtx falls back to a nil-AAD Open when the bound Open fails, so ciphertext written before context-binding was introduced keeps decrypting; new writes are bound, and legacy rows upgrade to a bound form on their next rewrite.

Design notes

  • One dependency. Only golang.org/x/crypto (for Argon2id); everything else is the standard library.
  • Master key stays yours. cryptoenv takes a 32-byte key you supply from your own secret store (env, KMS, Vault); the library never persists or logs it.
  • Tested. go test ./... covers hashing, round-trips, the AAD substitution attack, and the legacy-blob fallback.

Testing

go test ./...

License

MIT — see LICENSE.

About

Go library for storing credentials at rest — argon2id hashing, AES-256-GCM envelope encryption, and context-bound AAD that blocks ciphertext-relocation attacks.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages