Files
antonym/server/lib/crypto.ts
Dorian 54500a68e6 feat: scaffold Antonym fashion store
Anonymous Bitcoin-only fashion e-commerce with:
- Vue 3 + Tailwind 4 frontend with glassmorphism dark/light design system
- Express 5 + SQLite backend with BTCPay Server integration
- Nostr identity (NIP-07/keypair) for anonymous purchase tracking
- ChaCha20-Poly1305 encrypted shipping addresses
- Admin panel with order/product/stock management
- SVG logo splash animation with clip-path reveal
- 5 seeded products across 4 categories

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 00:23:21 +00:00

34 lines
1.2 KiB
TypeScript

import crypto from 'node:crypto'
const ALGORITHM = 'chacha20-poly1305'
const NONCE_LENGTH = 12
const TAG_LENGTH = 16
function getKey(): Buffer {
const hex = process.env.ENCRYPTION_KEY
if (!hex || hex.length !== 64) {
throw new Error('ENCRYPTION_KEY must be a 64-character hex string (32 bytes)')
}
return Buffer.from(hex, 'hex')
}
export function encrypt(plaintext: string): string {
const key = getKey()
const nonce = crypto.randomBytes(NONCE_LENGTH)
const cipher = crypto.createCipheriv(ALGORITHM, key, nonce, { authTagLength: TAG_LENGTH })
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()])
const tag = cipher.getAuthTag()
return Buffer.concat([nonce, tag, encrypted]).toString('hex')
}
export function decrypt(ciphertext: string): string {
const key = getKey()
const buf = Buffer.from(ciphertext, 'hex')
const nonce = buf.subarray(0, NONCE_LENGTH)
const tag = buf.subarray(NONCE_LENGTH, NONCE_LENGTH + TAG_LENGTH)
const encrypted = buf.subarray(NONCE_LENGTH + TAG_LENGTH)
const decipher = crypto.createDecipheriv(ALGORITHM, key, nonce, { authTagLength: TAG_LENGTH })
decipher.setAuthTag(tag)
return decipher.update(encrypted) + decipher.final('utf8')
}