← back to AbramsOS
tests/crypto.test.js
66 lines
// AES-256-GCM crypto helper tests.
// We need a 64-hex-char ENCRYPTION_KEY in env; load it from .env if present,
// otherwise inject a deterministic test key.
const test = require('node:test');
const assert = require('node:assert');
require('dotenv').config();
if (!process.env.ENCRYPTION_KEY || process.env.ENCRYPTION_KEY.length < 64) {
process.env.ENCRYPTION_KEY = 'a'.repeat(64);
}
const { encrypt, decrypt } = require('../lib/crypto');
test('round-trip plaintext returns same value', () => {
const plain = 'gmail-refresh-token-1//04abc-supersecret';
const { ciphertext, iv, tag } = encrypt(plain);
const decoded = decrypt(ciphertext, iv, tag);
assert.strictEqual(decoded, plain);
});
test('IV differs between encryptions of the same plaintext', () => {
const plain = 'same-plaintext';
const a = encrypt(plain);
const b = encrypt(plain);
assert.notStrictEqual(a.iv.toString('hex'), b.iv.toString('hex'), 'IVs should be random');
// Both decrypt back to the same plaintext
assert.strictEqual(decrypt(a.ciphertext, a.iv, a.tag), plain);
assert.strictEqual(decrypt(b.ciphertext, b.iv, b.tag), plain);
});
test('tampering with ciphertext throws', () => {
const { ciphertext, iv, tag } = encrypt('hello');
const tampered = Buffer.from(ciphertext);
tampered[0] ^= 0xff;
assert.throws(() => decrypt(tampered, iv, tag), /Unsupported state|unable to authenticate/i);
});
test('tampering with auth tag throws', () => {
const { ciphertext, iv, tag } = encrypt('hello');
const tamperedTag = Buffer.from(tag);
tamperedTag[0] ^= 0xff;
assert.throws(() => decrypt(ciphertext, iv, tamperedTag), /Unsupported state|unable to authenticate/i);
});
test('handles empty string', () => {
const { ciphertext, iv, tag } = encrypt('');
assert.strictEqual(decrypt(ciphertext, iv, tag), '');
});
test('handles unicode', () => {
const plain = 'héllo · 世界 · 🌍';
const { ciphertext, iv, tag } = encrypt(plain);
assert.strictEqual(decrypt(ciphertext, iv, tag), plain);
});
test('encrypt rejects when ENCRYPTION_KEY is missing or too short', () => {
const orig = process.env.ENCRYPTION_KEY;
process.env.ENCRYPTION_KEY = '';
// Re-require to pick up env change — but our crypto reads env at call time, so just call:
assert.throws(() => encrypt('x'), /ENCRYPTION_KEY/);
process.env.ENCRYPTION_KEY = '0123456789abcdef'; // 16 chars (too short)
assert.throws(() => encrypt('x'), /ENCRYPTION_KEY/);
process.env.ENCRYPTION_KEY = orig;
});