@iam-protocol/pulse-sdk 0.3.5 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +20 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +20 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/pulse.ts +37 -15
package/package.json
CHANGED
package/src/pulse.ts
CHANGED
|
@@ -40,9 +40,18 @@ async function extractFeatures(data: SensorData): Promise<number[]> {
|
|
|
40
40
|
const audioFeatures = await extractSpeakerFeatures(data.audio);
|
|
41
41
|
|
|
42
42
|
const hasMotion = data.motion.length >= MIN_MOTION_SAMPLES;
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
const hasTouch = data.touch.length >= MIN_TOUCH_SAMPLES;
|
|
44
|
+
|
|
45
|
+
// On mobile (both IMU and touch available), use touch/pointer dynamics for
|
|
46
|
+
// kinematic features. Stationary IMU reads constant gravity — the derivatives
|
|
47
|
+
// are near-zero and produce identical features across sessions. Finger tracing
|
|
48
|
+
// has natural inter-session variance because no two paths are identical.
|
|
49
|
+
const motionFeatures =
|
|
50
|
+
hasMotion && hasTouch
|
|
51
|
+
? extractMouseDynamics(data.touch)
|
|
52
|
+
: hasMotion
|
|
53
|
+
? extractMotionFeatures(data.motion)
|
|
54
|
+
: extractMouseDynamics(data.touch);
|
|
46
55
|
|
|
47
56
|
const touchFeatures = extractTouchFeatures(data.touch);
|
|
48
57
|
return fuseFeatures(audioFeatures, motionFeatures, touchFeatures);
|
|
@@ -92,6 +101,19 @@ async function processSensorData(
|
|
|
92
101
|
};
|
|
93
102
|
}
|
|
94
103
|
|
|
104
|
+
// Re-verification requires audio + at least one other modality.
|
|
105
|
+
// Audio-only fingerprints lack inter-session variance from motion/touch,
|
|
106
|
+
// producing identical SimHash results that fail the min_distance constraint.
|
|
107
|
+
const hasPreviousData = loadVerificationData() !== null;
|
|
108
|
+
if (hasPreviousData && !hasMotion && !hasTouch) {
|
|
109
|
+
return {
|
|
110
|
+
success: false,
|
|
111
|
+
commitment: new Uint8Array(32),
|
|
112
|
+
isFirstVerification: false,
|
|
113
|
+
error: "Insufficient sensor data for re-verification. Please trace the curve and allow motion access.",
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
95
117
|
// Extract features
|
|
96
118
|
const features = await extractFeatures(sensorData);
|
|
97
119
|
|
|
@@ -432,30 +454,30 @@ export class PulseSDK {
|
|
|
432
454
|
const session = this.createSession(touchElement);
|
|
433
455
|
const stopPromises: Promise<void>[] = [];
|
|
434
456
|
|
|
435
|
-
//
|
|
457
|
+
// Motion first — requires user gesture on iOS (gesture expires after getUserMedia)
|
|
436
458
|
try {
|
|
437
|
-
await session.
|
|
459
|
+
await session.startMotion();
|
|
460
|
+
} catch {
|
|
461
|
+
/* unexpected error — motion already skipped or idle */
|
|
462
|
+
}
|
|
463
|
+
if (session.isMotionCapturing()) {
|
|
438
464
|
stopPromises.push(
|
|
439
465
|
new Promise<void>((r) => setTimeout(r, DEFAULT_CAPTURE_MS))
|
|
440
|
-
.then(() => session.
|
|
466
|
+
.then(() => session.stopMotion())
|
|
441
467
|
.then(() => {})
|
|
442
468
|
);
|
|
443
|
-
} catch (err: any) {
|
|
444
|
-
throw new Error(`Audio capture failed: ${err?.message ?? "microphone unavailable"}`);
|
|
445
469
|
}
|
|
446
470
|
|
|
447
|
-
//
|
|
471
|
+
// Audio second — getUserMedia works without a gesture on secure origins
|
|
448
472
|
try {
|
|
449
|
-
await session.
|
|
450
|
-
} catch {
|
|
451
|
-
/* unexpected error — motion already skipped or idle */
|
|
452
|
-
}
|
|
453
|
-
if (session.isMotionCapturing()) {
|
|
473
|
+
await session.startAudio();
|
|
454
474
|
stopPromises.push(
|
|
455
475
|
new Promise<void>((r) => setTimeout(r, DEFAULT_CAPTURE_MS))
|
|
456
|
-
.then(() => session.
|
|
476
|
+
.then(() => session.stopAudio())
|
|
457
477
|
.then(() => {})
|
|
458
478
|
);
|
|
479
|
+
} catch (err: any) {
|
|
480
|
+
throw new Error(`Audio capture failed: ${err?.message ?? "microphone unavailable"}`);
|
|
459
481
|
}
|
|
460
482
|
|
|
461
483
|
// Touch
|