mirror your GitHub repos to tangled.org automatically
1import crypto from 'node:crypto'
2import { afterEach, beforeEach, describe, expect, it } from 'vitest'
3import { clearEncryptionKeyCache, decrypt, encrypt } from '../../server/utils/encryption'
4
5const ORIGINAL_ENV = process.env.NUXT_ENCRYPTION_KEY
6
7describe('encryption', () => {
8 beforeEach(() => {
9 process.env.NUXT_ENCRYPTION_KEY = crypto.randomBytes(32).toString('base64')
10 clearEncryptionKeyCache()
11 })
12
13 afterEach(() => {
14 if (ORIGINAL_ENV === undefined) delete process.env.NUXT_ENCRYPTION_KEY
15 else process.env.NUXT_ENCRYPTION_KEY = ORIGINAL_ENV
16 clearEncryptionKeyCache()
17 })
18
19 it('round-trips a string', () => {
20 const { ciphertext, nonce } = encrypt('hello world')
21 expect(decrypt(ciphertext, nonce)).toBe('hello world')
22 })
23
24 it('round-trips JSON-shaped data', () => {
25 const payload = JSON.stringify({ did: 'did:plc:foo', token: 'abc.def.ghi' })
26 const { ciphertext, nonce } = encrypt(payload)
27 expect(JSON.parse(decrypt(ciphertext, nonce))).toEqual({
28 did: 'did:plc:foo',
29 token: 'abc.def.ghi',
30 })
31 })
32
33 it('produces different ciphertext for the same input (random nonce)', () => {
34 const a = encrypt('same plaintext')
35 const b = encrypt('same plaintext')
36 expect(a.ciphertext.equals(b.ciphertext)).toBe(false)
37 expect(a.nonce.equals(b.nonce)).toBe(false)
38 })
39
40 it('throws on tampered ciphertext', () => {
41 const { ciphertext, nonce } = encrypt('hello')
42 const tampered = Buffer.from(ciphertext)
43 tampered[0] = tampered[0] ^ 0xFF
44 expect(() => decrypt(tampered, nonce)).toThrow(/invalid tag|auth/i)
45 })
46
47 it('throws on tampered nonce', () => {
48 const { ciphertext, nonce } = encrypt('hello')
49 const tampered = Buffer.from(nonce)
50 tampered[0] = tampered[0] ^ 0xFF
51 expect(() => decrypt(ciphertext, tampered)).toThrow(/invalid tag|auth/i)
52 })
53
54 it('throws when the key changes between encrypt and decrypt', () => {
55 const { ciphertext, nonce } = encrypt('hello')
56
57 // Rotate the key.
58 process.env.NUXT_ENCRYPTION_KEY = crypto.randomBytes(32).toString('base64')
59 clearEncryptionKeyCache()
60
61 expect(() => decrypt(ciphertext, nonce)).toThrow(/invalid tag|auth/i)
62 })
63
64 it('throws on wrong key length', () => {
65 process.env.NUXT_ENCRYPTION_KEY = Buffer.from('too short').toString('base64')
66 clearEncryptionKeyCache()
67 expect(() => encrypt('hello')).toThrow(/32 bytes/)
68 })
69
70 it('throws when the key is missing', () => {
71 delete process.env.NUXT_ENCRYPTION_KEY
72 clearEncryptionKeyCache()
73 expect(() => encrypt('hello')).toThrow(/NUXT_ENCRYPTION_KEY/)
74 })
75})