mirror of
https://github.com/kccleoc/seedpgp-web.git
synced 2026-03-07 09:57:50 +08:00
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:
73
src/lib/interactionEntropy.ts
Normal file
73
src/lib/interactionEntropy.ts
Normal 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 };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user