clone

a browser-first diagram editor with live svg previews, local drafts and encrypted, accountless sharing

main
diagramzip / apps / editor / test / encryption.test.js
encryption.test.js 2.9K
1import assert from 'node:assert/strict'2import test from 'node:test'3import {4 createLockedState,5 decryptLockedRender,6 encryptLockedRender,7 encryptLockedState,8 unlockLockedAlias,9 unwrapBundleKey,10 wrapBundleKey,11} from '../src/encryption.js'1213const state = {14 type: 'd2',15 source: 'a -> b',16 options: { theme: 200 },17 meta: { title: 'Private map', description: 'Secret topology.' },18 presentation: { background: '#ffffff', padding: 16, frame: true, appearance: 'raw' },19}2021test('encrypts and unlocks a diagram with no plaintext in the payload', async () => {22 const { bundleKey, payload } = await createLockedState(state, 'correct horse battery staple')23 assert.equal(payload.mode, 'locked')24 assert.equal(JSON.stringify(payload).includes(state.source), false)25 assert.equal(JSON.stringify(payload).includes(state.meta.title), false)2627 const unlocked = await unlockLockedAlias(payload, 'correct horse battery staple')28 assert.deepEqual(unlocked.state, state)29 assert.deepEqual(unlocked.bundleKey, bundleKey)30})3132test('rejects the wrong password', async () => {33 const { payload } = await createLockedState(state, 'correct horse battery staple')34 await assert.rejects(unlockLockedAlias(payload, 'incorrect password'), /Wrong password/)35})3637test('uses fresh IVs when encrypting the same state', async () => {38 const { bundleKey, payload } = await createLockedState(state, 'correct horse battery staple')39 const next = await encryptLockedState(state, bundleKey, payload.keyEnvelope)40 assert.notEqual(next.encryptedContent.iv, payload.encryptedContent.iv)41 assert.notEqual(next.encryptedMetadata.iv, payload.encryptedMetadata.iv)42})4344test('changes a password by rewrapping only the bundle key', async () => {45 const { bundleKey, payload } = await createLockedState(state, 'correct horse battery staple')46 const encryptedContent = structuredClone(payload.encryptedContent)47 const encryptedMetadata = structuredClone(payload.encryptedMetadata)48 const keyEnvelope = await wrapBundleKey(bundleKey, 'a completely different password')49 await assert.rejects(unwrapBundleKey(keyEnvelope, 'correct horse battery staple'))50 assert.deepEqual(await unwrapBundleKey(keyEnvelope, 'a completely different password'), bundleKey)51 assert.deepEqual(payload.encryptedContent, encryptedContent)52 assert.deepEqual(payload.encryptedMetadata, encryptedMetadata)53})5455test('encrypts a cached image with the same diagram bundle key', async () => {56 const { bundleKey } = await createLockedState(state, 'correct horse battery staple')57 const svg = new Blob(['<svg xmlns="http://www.w3.org/2000/svg"></svg>'], { type: 'image/svg+xml' })58 const encrypted = await encryptLockedRender(svg, bundleKey, 'svg')59 assert.equal(JSON.stringify(encrypted).includes('<svg'), false)60 const decrypted = await decryptLockedRender(encrypted, bundleKey, 'svg')61 assert.equal(decrypted.type, 'image/svg+xml')62 assert.equal(await decrypted.text(), await svg.text())63})