'use strict'; const assert = require('node:assert/strict'); const test = require('node:test'); const { Boss, SymmetricKey } = require('unicrypto'); const { createCrypstie, decryptCrypstiePayload, encodeCrypstieKey, initializeCrypto, openCrypstie, parseCrypstieUrl, } = require('../src'); const KEY_BYTES = Uint8Array.from({ length: 32 }, (_, index) => index + 1); const KEY_TEXT = Buffer.from(KEY_BYTES) .toString('base64') .replaceAll('+', '.') .replaceAll('/', '_') .replace(/=+$/, ''); test('parses the legacy Crypstie URL format', () => { const parsed = parseCrypstieUrl(`https://crypstie.com/_sample#${KEY_TEXT}`); assert.equal(parsed.id, 'sample'); assert.deepEqual(parsed.key, KEY_BYTES); }); test('encodes and decodes legacy Crypstie keys', () => { assert.deepEqual(parseCrypstieUrl(`https://crypstie.com/_x#${encodeCrypstieKey(KEY_BYTES)}`).key, KEY_BYTES); }); test('rejects a link without its fragment key', () => { assert.throws( () => parseCrypstieUrl('https://crypstie.com/_sample'), /no decryption key/, ); }); test('decrypts a BOSS payload produced in the Crypstie wire format', async () => { await initializeCrypto(); const key = new SymmetricKey({ keyBytes: KEY_BYTES }); const deleteAt = new Date('2030-01-02T03:04:05Z'); const packed = Boss.dump({ guid: 'sample', encryptedData: key.etaEncryptSync(new TextEncoder().encode('совместимость работает')), deleteAt, burnOnShow: false, showWide: true, syntaxHighlighting: 'text', editable: false, ownerHandle: null, }); const result = await decryptCrypstiePayload(packed, KEY_BYTES); assert.equal(result.text, 'совместимость работает'); assert.equal(result.id, 'sample'); assert.equal(result.deleteAt.toISOString(), deleteAt.toISOString()); assert.equal(result.burnOnShow, false); assert.equal(result.showWide, true); }); test('downloads the encrypted payload without sending the fragment key', async () => { await initializeCrypto(); const key = new SymmetricKey({ keyBytes: KEY_BYTES }); const packed = Boss.dump({ guid: 'sample', encryptedData: key.etaEncryptSync(new TextEncoder().encode('secret')), deleteAt: new Date('2030-01-02T03:04:05Z'), burnOnShow: false, showWide: false, editable: false, }); let requested; const fakeFetch = async (url) => { requested = url.href; return new Response(packed, { status: 200 }); }; const result = await openCrypstie( `https://crypstie.com/_sample#${KEY_TEXT}`, { fetch: fakeFetch }, ); assert.equal(requested, 'https://crypstie.com/api/crypstie/sample'); assert.equal(requested.includes(KEY_TEXT), false); assert.equal(result.text, 'secret'); }); test('creates a server-compatible encrypted BOSS payload', async () => { let request; const fakeFetch = async (url, options) => { request = { url: url.href, options }; return new Response('new_id', { status: 200 }); }; const deleteAt = new Date(Date.now() + 60_000); const result = await createCrypstie('created by node', { key: KEY_BYTES, deleteAt, rootUrl: 'https://example.test/base', burnOnShow: true, fetch: fakeFetch, }); assert.equal(request.url, 'https://example.test/api/crypstie'); assert.equal(request.options.method, 'POST'); assert.equal(result.url, `https://example.test/_new_id#${KEY_TEXT}`); assert.equal(result.burnOnShow, true); const uploaded = Boss.load(new Uint8Array(await request.options.body.get('data').arrayBuffer())); assert.equal(uploaded.burnOnShow, true); assert.equal( uploaded.deleteAt.toISOString(), new Date(Math.floor(deleteAt.getTime() / 1000) * 1000).toISOString(), ); assert.equal( new TextDecoder().decode(new SymmetricKey({ keyBytes: KEY_BYTES }).etaDecryptSync(uploaded.encryptedData)), 'created by node', ); }); test('reports authentication failure for a wrong key', async () => { await initializeCrypto(); const key = new SymmetricKey({ keyBytes: KEY_BYTES }); const packed = Boss.dump({ encryptedData: key.etaEncryptSync(new TextEncoder().encode('secret')) }); const wrongKey = Uint8Array.from(KEY_BYTES, (byte) => byte ^ 0xff); await assert.rejects(() => decryptCrypstiePayload(packed, wrongKey), /authenticate or decrypt/); });