From harness-claude
Implements Node.js crypto patterns for hashing, HMAC signatures, scrypt password hashing, secure random generation, and AES-256-GCM encryption/decryption. Useful for data integrity, API auth, and sensitive data protection.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Implement hashing, HMAC, signing, encryption, and key derivation with Node.js crypto
Applies cryptographic best practices for password hashing (bcrypt/Argon2), AES-256-GCM encryption, secure random generation, HMAC, digital signing, and key management using Node.js crypto.
Guides secure cryptography: hashing (Argon2id, bcrypt), encryption (AES-256-GCM), key management, JWT signing, TLS hardening, digital signatures for sensitive data.
Provides cryptography guidance on encryption (AES-256-GCM, ChaCha20), password hashing (Argon2id, bcrypt), signatures (Ed25519), TLS config, key management. Use for implementing or reviewing crypto.
Share bugs, ideas, or general feedback.
Implement hashing, HMAC, signing, encryption, and key derivation with Node.js crypto
import { createHash } from 'node:crypto';
function sha256(data: string): string {
return createHash('sha256').update(data).digest('hex');
}
import { createHmac, timingSafeEqual } from 'node:crypto';
function verifyWebhookSignature(payload: string, signature: string, secret: string): boolean {
const expected = createHmac('sha256', secret).update(payload).digest('hex');
const sig = Buffer.from(signature, 'hex');
const exp = Buffer.from(expected, 'hex');
if (sig.length !== exp.length) return false;
return timingSafeEqual(sig, exp);
}
import { scrypt, randomBytes } from 'node:crypto';
import { promisify } from 'node:util';
const scryptAsync = promisify(scrypt);
async function hashPassword(password: string): Promise<string> {
const salt = randomBytes(16).toString('hex');
const derivedKey = (await scryptAsync(password, salt, 64)) as Buffer;
return `${salt}:${derivedKey.toString('hex')}`;
}
async function verifyPassword(password: string, hash: string): Promise<boolean> {
const [salt, key] = hash.split(':');
const derivedKey = (await scryptAsync(password, salt, 64)) as Buffer;
return timingSafeEqual(Buffer.from(key, 'hex'), derivedKey);
}
import { randomBytes, randomUUID, randomInt } from 'node:crypto';
const token = randomBytes(32).toString('hex'); // 64-char hex string
const uuid = randomUUID(); // UUID v4
const pin = randomInt(100000, 999999); // 6-digit number
import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';
function encrypt(plaintext: string, key: Buffer): { iv: string; ciphertext: string; tag: string } {
const iv = randomBytes(12);
const cipher = createCipheriv('aes-256-gcm', key, iv);
let ciphertext = cipher.update(plaintext, 'utf-8', 'hex');
ciphertext += cipher.final('hex');
const tag = cipher.getAuthTag().toString('hex');
return { iv: iv.toString('hex'), ciphertext, tag };
}
function decrypt(encrypted: { iv: string; ciphertext: string; tag: string }, key: Buffer): string {
const decipher = createDecipheriv('aes-256-gcm', key, Buffer.from(encrypted.iv, 'hex'));
decipher.setAuthTag(Buffer.from(encrypted.tag, 'hex'));
let plaintext = decipher.update(encrypted.ciphertext, 'hex', 'utf-8');
plaintext += decipher.final('utf-8');
return plaintext;
}
timingSafeEqual for comparing secrets to prevent timing attacks:import { timingSafeEqual } from 'node:crypto';
// Both buffers must have the same length
const a = Buffer.from(inputToken, 'hex');
const b = Buffer.from(storedToken, 'hex');
const isValid = a.length === b.length && timingSafeEqual(a, b);
Node.js crypto provides cryptographic functions backed by OpenSSL. It supports symmetric encryption, asymmetric encryption, hashing, HMACs, key derivation, and random number generation.
Algorithm choices:
argon2 package)Never use: MD5 or SHA-1 for security-sensitive operations. They have known collision attacks.
timingSafeEqual is critical. Regular string comparison (===) leaks information about how many bytes match through timing. timingSafeEqual takes constant time regardless of how many bytes match.
Trade-offs:
randomBytes is cryptographically secure — but slower than Math.random(). Use Math.random() only for non-security purposeshttps://nodejs.org/api/crypto.html