22 lines
1.5 KiB
TypeScript
22 lines
1.5 KiB
TypeScript
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();
|
|
});
|
|
});
|