Fix CameraEntropy video initialization and add stats review panel

- Fix videoRef timing issue by using useEffect for video setup
- Stop animation loop on capture to prevent infinite warnings
- Fix null canvas reference in generateMnemonicFromEntropy
- Add stats review panel with continue/retake options
- Add seed generation explanation and blurred preview
- Implement seed generation from camera noise/entropy bits and enhance dice rolls with detailed statistical analysis
This commit is contained in:
LC mac
2026-02-10 00:15:49 +08:00
parent 185efe454f
commit 586eabc361
6 changed files with 1095 additions and 175 deletions

View File

@@ -0,0 +1,73 @@
/**
* Collects entropy from user interactions (mouse, keyboard, touch)
* Runs in background to enhance any entropy generation method
*/
export class InteractionEntropy {
private samples: number[] = [];
private lastEvent = 0;
private startTime = performance.now();
private sources = { mouse: 0, keyboard: 0, touch: 0 };
constructor() {
this.initListeners();
}
private initListeners() {
const handleEvent = (e: MouseEvent | KeyboardEvent | TouchEvent) => {
const now = performance.now();
const delta = now - this.lastEvent;
if (delta > 0 && delta < 10000) { // Ignore huge gaps
this.samples.push(delta);
if (e instanceof MouseEvent) {
this.samples.push(e.clientX ^ e.clientY);
this.sources.mouse++;
} else if (e instanceof KeyboardEvent) {
this.samples.push(e.key.codePointAt(0) ?? 0);
this.sources.keyboard++;
} else if (e instanceof TouchEvent && e.touches[0]) {
this.samples.push(e.touches[0].clientX ^ e.touches[0].clientY);
this.sources.touch++;
}
}
this.lastEvent = now;
// Keep last 256 samples (128 pairs)
if (this.samples.length > 256) {
this.samples.splice(0, this.samples.length - 256);
}
};
document.addEventListener('mousemove', handleEvent);
document.addEventListener('keydown', handleEvent);
document.addEventListener('touchmove', handleEvent);
}
async getEntropyBytes(): Promise<Uint8Array> {
// Convert samples to entropy via SHA-256
const data = new TextEncoder().encode(
this.samples.join(',') + performance.now()
);
const hash = await crypto.subtle.digest('SHA-256', data);
return new Uint8Array(hash);
}
getSampleCount(): { mouse: number; keyboard: number; touch: number; total: number } {
return {
...this.sources,
total: this.sources.mouse + this.sources.keyboard + this.sources.touch
};
}
getCollectionTime(): number {
return performance.now() - this.startTime;
}
clear() {
this.samples = [];
this.lastEvent = 0;
this.startTime = performance.now();
this.sources = { mouse: 0, keyboard: 0, touch: 0 };
}
}