Skip to content

hameddk/secret-storage

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@hameddk/secret-storage

AES-256-GCM encrypt/decrypt for storing secrets at rest. Pure crypto, zero dependencies, Node 18+.

npm install @hameddk/secret-storage

Usage

import { encrypt, decrypt } from '@hameddk/secret-storage';
import { randomBytes } from 'node:crypto';

// Generate a key once and store it somewhere safe
// (env var, secrets manager, KMS — your call)
const key = randomBytes(32).toString('hex');

const ciphertext = encrypt('hello world', key);
// "v1:4b2f9c...:9a1e7d...:c8d3a2..."

const plaintext = decrypt(ciphertext, key);
// "hello world"

The key parameter can be either a 32-byte Buffer or a 64-character hex string. Either way, you provide the key — this library never reads from process.env or the filesystem.

API

encrypt(plaintext, key) => string

Param Type Description
plaintext string UTF-8 string to encrypt. Empty string is allowed.
key Buffer | string 32-byte Buffer or 64-char hex string.

Returns ciphertext in the format <iv-hex>:<tag-hex>:<data-hex>.

Throws:

  • InvalidKeyError — key is not 32 bytes / not valid hex / wrong length.
  • TypeError — plaintext is not a string.

decrypt(ciphertext, key) => string

Param Type Description
ciphertext string Output from a prior encrypt() call.
key Buffer | string Same key used for encryption.

Returns the original UTF-8 plaintext.

Throws:

  • InvalidKeyError — key is malformed.
  • MalformedCiphertextError — wrong number of parts, non-hex bytes.
  • DecryptionError — auth tag mismatch (wrong key, or ciphertext was tampered with).

Error classes

import {
  InvalidKeyError,
  MalformedCiphertextError,
  DecryptionError,
} from '@hameddk/secret-storage';

try {
  decrypt(ct, key);
} catch (err) {
  if (err instanceof DecryptionError) {
    // Wrong key or tampered data
  } else if (err instanceof MalformedCiphertextError) {
    // Input wasn't a valid ciphertext string
  }
}

Storage format

encrypt() always emits the versioned format:

v1:<iv-hex>:<tag-hex>:<ciphertext-hex>
Part Bytes Hex chars Notes
v1 2 Format version literal — currently the only one
IV 12 24 Random per encryption (NIST SP 800-38D recommended length)
Auth tag 16 32 GCM authentication tag
Ciphertext variable 2× plaintext bytes Encrypted body (may be empty)

The format is documented as a stable contract. You can decrypt manually with Node crypto if you ever need to migrate away from this package.

Legacy unversioned format

For backward compatibility, decrypt() also accepts the unversioned three-part format:

<iv-hex>:<tag-hex>:<ciphertext-hex>

This is what older codebases (including the project this module was extracted from) produced before the version prefix was introduced. IV length is parsed from the stored hex, so legacy ciphertexts with 16-byte IVs decrypt correctly. encrypt() never produces this format — only decrypt() accepts it as input.

If a future version of the format is introduced, decrypt() will route on the v<N>: prefix; the unversioned legacy form will continue to map to AES-256-GCM as it does today.

Security notes

  • Bring your own key. This library never generates, persists, rotates, or fetches keys. That's deliberately out of scope. Use crypto.randomBytes(32) or a KMS to produce one.
  • AES-256-GCM provides confidentiality and integrity. Tampering with the ciphertext or auth tag causes decrypt() to throw DecryptionError.
  • Random IV per call. Reusing an IV with the same key catastrophically breaks GCM. This library generates a fresh 12-byte IV via crypto.randomBytes() on every encrypt() call.
  • No key rotation built in. If you need to rotate a key, decrypt-then-re-encrypt at your storage layer.
  • Not for streaming or large files. This is a memory-only API — both plaintext and ciphertext fit in a string.

License

MIT — see LICENSE.

About

AES-256-GCM authenticated encryption for storing secrets at rest. Zero dependencies, no key management.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors