feat(web): add account workspaces and signed content flows

This commit is contained in:
justme committed 2026-10-14 04:06:03 +08:00
1 parent 7b8d61d1ba
commit b5cf3d5958
117 files changed
+2789 -1674

No files matched your search

@@ -0,0 +1,21 @@
import { describe, expect, it } from 'vitest';
import { openPasskeyKey, sealPasskeyKey } from './passkeyCrypto';
import { bytes, openJson, sealJson } from './accountCrypto';
describe('Passkey recovery key envelopes', () => {
it('recovers the same non-exportable vault key without exposing plaintext to the backend', async () => {
const material = crypto.getRandomValues(new Uint8Array(32)); const prf = crypto.getRandomValues(new Uint8Array(32));
const key = await crypto.subtle.importKey('raw', bytes(material), 'AES-GCM', false, ['encrypt', 'decrypt']);
const vault = await sealJson({ private: 'test-private-identity' }, key);
const envelope = await sealPasskeyKey(material, bytes(prf), 'alice', 'credential-a');
expect(envelope).not.toContain('test-private-identity');
const recovered = await openPasskeyKey(envelope, bytes(prf), 'alice', 'credential-a');
expect(recovered.extractable).toBe(false); expect(await openJson(vault, recovered)).toEqual({ private: 'test-private-identity' });
await expect(openPasskeyKey(envelope, bytes(prf), 'bob', 'credential-a')).rejects.toThrow();
await expect(openPasskeyKey(envelope, bytes(prf), 'alice', 'credential-b')).rejects.toThrow();
await expect(openPasskeyKey(envelope, bytes(crypto.getRandomValues(new Uint8Array(32))), 'alice', 'credential-a')).rejects.toThrow();
});
it('rejects malformed PRF outputs', async () => {
await expect(sealPasskeyKey(new Uint8Array(32), new ArrayBuffer(31), 'alice', 'credential')).rejects.toThrow();
});
});