import { describe, expect, it } from 'vitest'; import { decryptLogin, encryptLogin } from './credentialCipher'; describe('local encrypted login fields', () => { const endpoint = 'https://drop.test/api/v1/'; const fields = { username: 'alice', password: 'a-test-password' }; it('restores fields using a non-exportable key while keeping plaintext out of the record', async () => { const value = await encryptLogin(fields, endpoint); expect(value.key.extractable).toBe(false); expect(new TextDecoder().decode(value.ciphertext)).not.toContain(fields.password); expect(await decryptLogin(value, endpoint)).toEqual(fields); await expect(crypto.subtle.exportKey('raw', value.key)).rejects.toThrow(); }); it('binds a saved credential to its API server and rejects tampering', async () => { const value = await encryptLogin(fields, endpoint); await expect(decryptLogin(value, 'https://other.test/api/v1/')).rejects.toThrow(); const bytes = new Uint8Array(value.ciphertext); bytes[0] = (bytes[0] ?? 0) ^ 1; await expect(decryptLogin({ ...value, ciphertext: bytes.buffer }, endpoint)).rejects.toThrow(); }); it('uses independent random encryption material for repeated saves', async () => { const first = await encryptLogin(fields, endpoint); const second = await encryptLogin(fields, endpoint); expect(first.iv).not.toEqual(second.iv); expect(new Uint8Array(first.ciphertext)).not.toEqual(new Uint8Array(second.ciphertext)); }); });