69673e7727
Groundwork for offline pass-and-play (hotseat) seat/host PIN locks: - lib/pin.ts: salted SHA-256 PinLock (newSalt/hashPin/newLock/verifyPin); a social lock for a shared device (documented, not cryptography). - components/PinPad.svelte: 4-digit keypad (set/verify/change), auto-submit on the 4th digit, shake on mismatch, hardware-keyboard support. - i18n: pin.* keys (en + ru). Engine/source/UI wiring follows.
50 lines
1.5 KiB
TypeScript
50 lines
1.5 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { hashPin, newLock, newSalt, verifyPin } from './pin';
|
|
|
|
describe('hashPin', () => {
|
|
it('is deterministic for the same pin and salt', async () => {
|
|
const salt = 'fixedsalt';
|
|
expect(await hashPin('1234', salt)).toBe(await hashPin('1234', salt));
|
|
});
|
|
|
|
it('differs when the salt differs', async () => {
|
|
expect(await hashPin('1234', 'saltA')).not.toBe(await hashPin('1234', 'saltB'));
|
|
});
|
|
|
|
it('differs when the pin differs', async () => {
|
|
const salt = 'fixedsalt';
|
|
expect(await hashPin('1234', salt)).not.toBe(await hashPin('4321', salt));
|
|
});
|
|
});
|
|
|
|
describe('newSalt', () => {
|
|
it('returns a non-empty value', () => {
|
|
expect(newSalt().length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('returns a fresh value each call', () => {
|
|
expect(newSalt()).not.toBe(newSalt());
|
|
});
|
|
});
|
|
|
|
describe('newLock / verifyPin', () => {
|
|
it('produces a lock that verifies its own pin', async () => {
|
|
const lock = await newLock('1234');
|
|
expect(lock.hash.length).toBeGreaterThan(0);
|
|
expect(lock.salt.length).toBeGreaterThan(0);
|
|
expect(await verifyPin('1234', lock)).toBe(true);
|
|
});
|
|
|
|
it('rejects a wrong pin', async () => {
|
|
const lock = await newLock('1234');
|
|
expect(await verifyPin('0000', lock)).toBe(false);
|
|
});
|
|
|
|
it('salts each lock independently (same pin, different hashes)', async () => {
|
|
const a = await newLock('1234');
|
|
const b = await newLock('1234');
|
|
expect(a.salt).not.toBe(b.salt);
|
|
expect(a.hash).not.toBe(b.hash);
|
|
});
|
|
});
|