@entros/pulse-sdk 3.2.0 → 3.3.1
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.d.mts +182 -1
- package/dist/index.d.ts +182 -1
- package/dist/index.js +648 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +635 -20
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ __export(index_exports, {
|
|
|
33
33
|
DEFAULT_CAPTURE_MS: () => DEFAULT_CAPTURE_MS,
|
|
34
34
|
DEFAULT_MIN_DISTANCE: () => DEFAULT_MIN_DISTANCE,
|
|
35
35
|
DEFAULT_THRESHOLD: () => DEFAULT_THRESHOLD,
|
|
36
|
+
ENCRYPTED_BASELINE_BLOB_BYTES: () => ENCRYPTED_BASELINE_BLOB_BYTES,
|
|
36
37
|
FINGERPRINT_BITS: () => FINGERPRINT_BITS,
|
|
37
38
|
MAX_CAPTURE_MS: () => MAX_CAPTURE_MS,
|
|
38
39
|
MIN_AUDIO_SAMPLES: () => MIN_AUDIO_SAMPLES,
|
|
@@ -44,11 +45,19 @@ __export(index_exports, {
|
|
|
44
45
|
PulseSDK: () => PulseSDK,
|
|
45
46
|
PulseSession: () => PulseSession,
|
|
46
47
|
SPEAKER_FEATURE_COUNT: () => SPEAKER_FEATURE_COUNT,
|
|
48
|
+
StaleEncryptedBaselineError: () => StaleEncryptedBaselineError,
|
|
47
49
|
TOUCH_FEATURE_COUNT: () => TOUCH_FEATURE_COUNT,
|
|
48
50
|
attestAgentOperator: () => attestAgentOperator,
|
|
49
51
|
bigintToBytes32: () => bigintToBytes32,
|
|
52
|
+
bytes32ToBigint: () => bytes32ToBigint,
|
|
53
|
+
bytesToFingerprint: () => bytesToFingerprint,
|
|
54
|
+
clearBaselineKeyCache: () => clearBaselineKeyCache,
|
|
50
55
|
computeCommitment: () => computeCommitment,
|
|
56
|
+
decryptBaselineBlob: () => decryptBaselineBlob,
|
|
57
|
+
deriveBaselineKey: () => deriveBaselineKey,
|
|
58
|
+
deriveEncryptedBaselinePda: () => deriveEncryptedBaselinePda,
|
|
51
59
|
encodeAudioAsBase64: () => encodeAudioAsBase64,
|
|
60
|
+
encryptBaselineBlob: () => encryptBaselineBlob,
|
|
52
61
|
extractAccelerationMagnitude: () => extractAccelerationMagnitude,
|
|
53
62
|
extractMotionFeatures: () => extractMotionFeatures,
|
|
54
63
|
extractMouseDynamics: () => extractMouseDynamics,
|
|
@@ -56,7 +65,9 @@ __export(index_exports, {
|
|
|
56
65
|
extractSpeakerFeaturesDetailed: () => extractSpeakerFeaturesDetailed,
|
|
57
66
|
extractTouchFeatures: () => extractTouchFeatures,
|
|
58
67
|
fetchChallenge: () => fetchChallenge,
|
|
68
|
+
fetchEncryptedBaseline: () => fetchEncryptedBaseline,
|
|
59
69
|
fetchIdentityState: () => fetchIdentityState,
|
|
70
|
+
fingerprintToBytes: () => fingerprintToBytes,
|
|
60
71
|
fuseFeatures: () => fuseFeatures,
|
|
61
72
|
fuseRawFeatures: () => fuseRawFeatures,
|
|
62
73
|
generateLissajousPoints: () => generateLissajousPoints,
|
|
@@ -68,11 +79,13 @@ __export(index_exports, {
|
|
|
68
79
|
generateSolanaProof: () => generateSolanaProof,
|
|
69
80
|
generateTBH: () => generateTBH,
|
|
70
81
|
getAgentHumanOperator: () => getAgentHumanOperator,
|
|
82
|
+
getOrDeriveBaselineKey: () => getOrDeriveBaselineKey,
|
|
71
83
|
hammingDistance: () => hammingDistance,
|
|
72
84
|
loadVerificationData: () => loadVerificationData,
|
|
73
85
|
packBits: () => packBits,
|
|
74
86
|
prepareCircuitInput: () => prepareCircuitInput,
|
|
75
87
|
randomLissajousParams: () => randomLissajousParams,
|
|
88
|
+
recoverBaselineFromChain: () => recoverBaselineFromChain,
|
|
76
89
|
serializeProof: () => serializeProof,
|
|
77
90
|
simhash: () => simhash,
|
|
78
91
|
storeVerificationData: () => storeVerificationData,
|
|
@@ -2242,12 +2255,6 @@ var entros_anchor_default = {
|
|
|
2242
2255
|
spec: "0.1.0",
|
|
2243
2256
|
description: "Non-transferable identity token for Entros Protocol"
|
|
2244
2257
|
},
|
|
2245
|
-
docs: [
|
|
2246
|
-
"Mint account space for Token-2022 with NonTransferable extension.",
|
|
2247
|
-
"Base mint = 82 bytes, account type = 1 byte, extension type (2) + length (2) = 4 bytes,",
|
|
2248
|
-
"NonTransferable data = 0 bytes. Plus multisig padding from Token-2022.",
|
|
2249
|
-
"We use a constant derived from the Token-2022 spec."
|
|
2250
|
-
],
|
|
2251
2258
|
instructions: [
|
|
2252
2259
|
{
|
|
2253
2260
|
name: "authorize_new_wallet",
|
|
@@ -3229,6 +3236,132 @@ var entros_anchor_default = {
|
|
|
3229
3236
|
}
|
|
3230
3237
|
]
|
|
3231
3238
|
},
|
|
3239
|
+
{
|
|
3240
|
+
name: "set_encrypted_baseline",
|
|
3241
|
+
docs: [
|
|
3242
|
+
"Write or overwrite the caller's encrypted baseline blob.",
|
|
3243
|
+
"",
|
|
3244
|
+
"The blob is opaque to the program \u2014 AES-256-GCM ciphertext of the",
|
|
3245
|
+
"user's previous SimHash plus salt, produced off-chain in the SDK",
|
|
3246
|
+
"under a key derived from a deterministic `signMessage`. The GCM",
|
|
3247
|
+
"auth tag binds the blob to (wallet, this PDA's address, on-chain",
|
|
3248
|
+
"`current_commitment` at encryption time), so a stale blob produced",
|
|
3249
|
+
"before a `reset_identity_state` fails authentication under the new",
|
|
3250
|
+
"commitment and the SDK falls back to a fresh-capture flow.",
|
|
3251
|
+
"",
|
|
3252
|
+
"The program never decrypts the blob \u2014 it only stores opaque bytes.",
|
|
3253
|
+
"Plaintext biometric data never reaches chain at any point.",
|
|
3254
|
+
"",
|
|
3255
|
+
"Guards:",
|
|
3256
|
+
"* Signer must equal the wallet that seeds the EncryptedBaseline",
|
|
3257
|
+
"PDA (enforced by the seeds constraint on the Accounts struct).",
|
|
3258
|
+
'* The caller\'s IdentityState PDA at `[b"identity", signer]` must',
|
|
3259
|
+
"already exist \u2014 pre-mint attempts are rejected with",
|
|
3260
|
+
"`IdentityStateNotFound`. Anchor's seeds constraint validates the",
|
|
3261
|
+
"PDA address; the `data_len() > 0` check confirms initialization."
|
|
3262
|
+
],
|
|
3263
|
+
discriminator: [
|
|
3264
|
+
10,
|
|
3265
|
+
73,
|
|
3266
|
+
41,
|
|
3267
|
+
36,
|
|
3268
|
+
2,
|
|
3269
|
+
145,
|
|
3270
|
+
87,
|
|
3271
|
+
111
|
|
3272
|
+
],
|
|
3273
|
+
accounts: [
|
|
3274
|
+
{
|
|
3275
|
+
name: "authority",
|
|
3276
|
+
writable: true,
|
|
3277
|
+
signer: true
|
|
3278
|
+
},
|
|
3279
|
+
{
|
|
3280
|
+
name: "identity_state",
|
|
3281
|
+
docs: [
|
|
3282
|
+
"UncheckedAccount because we only need to verify the IdentityState",
|
|
3283
|
+
"PDA exists at the expected address \u2014 we don't need to deserialize",
|
|
3284
|
+
"its fields. The seeds constraint validates the PDA address."
|
|
3285
|
+
],
|
|
3286
|
+
pda: {
|
|
3287
|
+
seeds: [
|
|
3288
|
+
{
|
|
3289
|
+
kind: "const",
|
|
3290
|
+
value: [
|
|
3291
|
+
105,
|
|
3292
|
+
100,
|
|
3293
|
+
101,
|
|
3294
|
+
110,
|
|
3295
|
+
116,
|
|
3296
|
+
105,
|
|
3297
|
+
116,
|
|
3298
|
+
121
|
|
3299
|
+
]
|
|
3300
|
+
},
|
|
3301
|
+
{
|
|
3302
|
+
kind: "account",
|
|
3303
|
+
path: "authority"
|
|
3304
|
+
}
|
|
3305
|
+
]
|
|
3306
|
+
}
|
|
3307
|
+
},
|
|
3308
|
+
{
|
|
3309
|
+
name: "encrypted_baseline",
|
|
3310
|
+
docs: [
|
|
3311
|
+
"The EncryptedBaseline PDA. Created on first call, overwritten on",
|
|
3312
|
+
"subsequent calls. PDA seeds bind to the signer's wallet, so only",
|
|
3313
|
+
"the wallet owner can write to their own baseline."
|
|
3314
|
+
],
|
|
3315
|
+
writable: true,
|
|
3316
|
+
pda: {
|
|
3317
|
+
seeds: [
|
|
3318
|
+
{
|
|
3319
|
+
kind: "const",
|
|
3320
|
+
value: [
|
|
3321
|
+
101,
|
|
3322
|
+
110,
|
|
3323
|
+
99,
|
|
3324
|
+
114,
|
|
3325
|
+
121,
|
|
3326
|
+
112,
|
|
3327
|
+
116,
|
|
3328
|
+
101,
|
|
3329
|
+
100,
|
|
3330
|
+
95,
|
|
3331
|
+
98,
|
|
3332
|
+
97,
|
|
3333
|
+
115,
|
|
3334
|
+
101,
|
|
3335
|
+
108,
|
|
3336
|
+
105,
|
|
3337
|
+
110,
|
|
3338
|
+
101
|
|
3339
|
+
]
|
|
3340
|
+
},
|
|
3341
|
+
{
|
|
3342
|
+
kind: "account",
|
|
3343
|
+
path: "authority"
|
|
3344
|
+
}
|
|
3345
|
+
]
|
|
3346
|
+
}
|
|
3347
|
+
},
|
|
3348
|
+
{
|
|
3349
|
+
name: "system_program",
|
|
3350
|
+
address: "11111111111111111111111111111111"
|
|
3351
|
+
}
|
|
3352
|
+
],
|
|
3353
|
+
args: [
|
|
3354
|
+
{
|
|
3355
|
+
name: "blob",
|
|
3356
|
+
type: {
|
|
3357
|
+
array: [
|
|
3358
|
+
"u8",
|
|
3359
|
+
96
|
|
3360
|
+
]
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
]
|
|
3364
|
+
},
|
|
3232
3365
|
{
|
|
3233
3366
|
name: "update_anchor",
|
|
3234
3367
|
docs: [
|
|
@@ -3532,6 +3665,19 @@ var entros_anchor_default = {
|
|
|
3532
3665
|
}
|
|
3533
3666
|
],
|
|
3534
3667
|
accounts: [
|
|
3668
|
+
{
|
|
3669
|
+
name: "EncryptedBaseline",
|
|
3670
|
+
discriminator: [
|
|
3671
|
+
235,
|
|
3672
|
+
60,
|
|
3673
|
+
246,
|
|
3674
|
+
174,
|
|
3675
|
+
131,
|
|
3676
|
+
9,
|
|
3677
|
+
248,
|
|
3678
|
+
146
|
|
3679
|
+
]
|
|
3680
|
+
},
|
|
3535
3681
|
{
|
|
3536
3682
|
name: "IdentityState",
|
|
3537
3683
|
discriminator: [
|
|
@@ -3586,6 +3732,19 @@ var entros_anchor_default = {
|
|
|
3586
3732
|
59
|
|
3587
3733
|
]
|
|
3588
3734
|
},
|
|
3735
|
+
{
|
|
3736
|
+
name: "EncryptedBaselineSet",
|
|
3737
|
+
discriminator: [
|
|
3738
|
+
198,
|
|
3739
|
+
176,
|
|
3740
|
+
56,
|
|
3741
|
+
167,
|
|
3742
|
+
74,
|
|
3743
|
+
225,
|
|
3744
|
+
138,
|
|
3745
|
+
121
|
|
3746
|
+
]
|
|
3747
|
+
},
|
|
3589
3748
|
{
|
|
3590
3749
|
name: "MigrateIdentityEvent",
|
|
3591
3750
|
discriminator: [
|
|
@@ -3710,6 +3869,11 @@ var entros_anchor_default = {
|
|
|
3710
3869
|
code: 6021,
|
|
3711
3870
|
name: "MalformedReceiptMessage",
|
|
3712
3871
|
msg: "Receipt message has malformed length or layout"
|
|
3872
|
+
},
|
|
3873
|
+
{
|
|
3874
|
+
code: 6022,
|
|
3875
|
+
name: "IdentityStateNotFound",
|
|
3876
|
+
msg: "set_encrypted_baseline called before mint_anchor \u2014 IdentityState PDA does not exist"
|
|
3713
3877
|
}
|
|
3714
3878
|
],
|
|
3715
3879
|
types: [
|
|
@@ -3792,6 +3956,64 @@ var entros_anchor_default = {
|
|
|
3792
3956
|
]
|
|
3793
3957
|
}
|
|
3794
3958
|
},
|
|
3959
|
+
{
|
|
3960
|
+
name: "EncryptedBaseline",
|
|
3961
|
+
docs: [
|
|
3962
|
+
"Wallet-keyed encrypted baseline blob, stored at PDA seeds",
|
|
3963
|
+
'`[b"encrypted_baseline", wallet.key().as_ref()]`. Persists the user\'s',
|
|
3964
|
+
"previous SimHash + salt across cache wipes and device changes so the",
|
|
3965
|
+
"Hamming-distance ZK proof can recover its private witnesses on any",
|
|
3966
|
+
"device with the originating wallet.",
|
|
3967
|
+
"",
|
|
3968
|
+
"The blob is opaque ciphertext to the program \u2014 AES-256-GCM produced",
|
|
3969
|
+
"off-chain in the SDK under a key derived from a deterministic",
|
|
3970
|
+
"`signMessage` on a domain-separated payload. The GCM AAD binds the",
|
|
3971
|
+
"blob to (wallet, this PDA's address, current on-chain commitment),",
|
|
3972
|
+
"so a stale blob (post-`reset_identity_state`) fails authentication",
|
|
3973
|
+
"against the new commitment and the SDK falls back to a fresh-capture",
|
|
3974
|
+
"flow.",
|
|
3975
|
+
"",
|
|
3976
|
+
"The program never decrypts the blob \u2014 it only stores opaque bytes.",
|
|
3977
|
+
"Plaintext biometric data never reaches chain at any point."
|
|
3978
|
+
],
|
|
3979
|
+
type: {
|
|
3980
|
+
kind: "struct",
|
|
3981
|
+
fields: [
|
|
3982
|
+
{
|
|
3983
|
+
name: "blob",
|
|
3984
|
+
docs: [
|
|
3985
|
+
"96-byte versioned ciphertext envelope.",
|
|
3986
|
+
"Layout: version(1) || algo(1) || reserved(2) || iv(12) || ct+tag(80)."
|
|
3987
|
+
],
|
|
3988
|
+
type: {
|
|
3989
|
+
array: [
|
|
3990
|
+
"u8",
|
|
3991
|
+
96
|
|
3992
|
+
]
|
|
3993
|
+
}
|
|
3994
|
+
},
|
|
3995
|
+
{
|
|
3996
|
+
name: "bump",
|
|
3997
|
+
docs: [
|
|
3998
|
+
"PDA bump seed."
|
|
3999
|
+
],
|
|
4000
|
+
type: "u8"
|
|
4001
|
+
}
|
|
4002
|
+
]
|
|
4003
|
+
}
|
|
4004
|
+
},
|
|
4005
|
+
{
|
|
4006
|
+
name: "EncryptedBaselineSet",
|
|
4007
|
+
type: {
|
|
4008
|
+
kind: "struct",
|
|
4009
|
+
fields: [
|
|
4010
|
+
{
|
|
4011
|
+
name: "owner",
|
|
4012
|
+
type: "pubkey"
|
|
4013
|
+
}
|
|
4014
|
+
]
|
|
4015
|
+
}
|
|
4016
|
+
},
|
|
3795
4017
|
{
|
|
3796
4018
|
name: "IdentityState",
|
|
3797
4019
|
type: {
|
|
@@ -4521,7 +4743,250 @@ async function buildEd25519ReceiptIx(receipt) {
|
|
|
4521
4743
|
});
|
|
4522
4744
|
}
|
|
4523
4745
|
|
|
4746
|
+
// src/identity/baseline.ts
|
|
4747
|
+
var BLOB_VERSION = 1;
|
|
4748
|
+
var ALGORITHM_AES_256_GCM = 1;
|
|
4749
|
+
var ENCRYPTED_BASELINE_BLOB_BYTES = 96;
|
|
4750
|
+
var IV_BYTES = 12;
|
|
4751
|
+
var HEADER_BYTES = 4;
|
|
4752
|
+
var PLAINTEXT_BYTES = 64;
|
|
4753
|
+
function fingerprintToBytes(bits) {
|
|
4754
|
+
if (bits.length !== 256) {
|
|
4755
|
+
throw new Error(`expected 256-bit fingerprint, got ${bits.length}`);
|
|
4756
|
+
}
|
|
4757
|
+
const out = new Uint8Array(32);
|
|
4758
|
+
for (let i = 0; i < 256; i++) {
|
|
4759
|
+
if (bits[i] === 1) {
|
|
4760
|
+
out[i >> 3] = (out[i >> 3] ?? 0) | 1 << (i & 7);
|
|
4761
|
+
}
|
|
4762
|
+
}
|
|
4763
|
+
return out;
|
|
4764
|
+
}
|
|
4765
|
+
function bytesToFingerprint(bytes) {
|
|
4766
|
+
if (bytes.length !== 32) {
|
|
4767
|
+
throw new Error(`expected 32 bytes, got ${bytes.length}`);
|
|
4768
|
+
}
|
|
4769
|
+
const bits = new Array(256);
|
|
4770
|
+
for (let i = 0; i < 256; i++) {
|
|
4771
|
+
bits[i] = (bytes[i >> 3] ?? 0) >> (i & 7) & 1;
|
|
4772
|
+
}
|
|
4773
|
+
return bits;
|
|
4774
|
+
}
|
|
4775
|
+
function bytes32ToBigint(bytes) {
|
|
4776
|
+
if (bytes.length !== 32) {
|
|
4777
|
+
throw new Error(`expected 32 bytes, got ${bytes.length}`);
|
|
4778
|
+
}
|
|
4779
|
+
let val = BigInt(0);
|
|
4780
|
+
for (let i = 0; i < 32; i++) {
|
|
4781
|
+
val = val << BigInt(8) | BigInt(bytes[i] ?? 0);
|
|
4782
|
+
}
|
|
4783
|
+
return val;
|
|
4784
|
+
}
|
|
4785
|
+
function buildDomainMessage(walletPubkey) {
|
|
4786
|
+
return [
|
|
4787
|
+
"Entros Protocol \u2014 Identity Baseline Key Derivation",
|
|
4788
|
+
"",
|
|
4789
|
+
"By signing this message, you authorize Entros Protocol to derive",
|
|
4790
|
+
"an encryption key for your on-chain identity baseline. This is",
|
|
4791
|
+
"not a transaction.",
|
|
4792
|
+
"",
|
|
4793
|
+
`Wallet: ${walletPubkey.toBase58()}`,
|
|
4794
|
+
"Version: 1",
|
|
4795
|
+
"Domain: entros.io"
|
|
4796
|
+
].join("\n");
|
|
4797
|
+
}
|
|
4798
|
+
async function deriveEncryptedBaselinePda(walletPubkey) {
|
|
4799
|
+
const { PublicKey: PK } = await import("@solana/web3.js");
|
|
4800
|
+
const programId = new PK(PROGRAM_IDS.entrosAnchor);
|
|
4801
|
+
return PK.findProgramAddressSync(
|
|
4802
|
+
[new TextEncoder().encode("encrypted_baseline"), walletPubkey.toBuffer()],
|
|
4803
|
+
programId
|
|
4804
|
+
);
|
|
4805
|
+
}
|
|
4806
|
+
async function deriveBaselineKey(wallet) {
|
|
4807
|
+
if (typeof wallet.signMessage !== "function") {
|
|
4808
|
+
throw new Error("wallet does not support signMessage");
|
|
4809
|
+
}
|
|
4810
|
+
const message = buildDomainMessage(wallet.publicKey);
|
|
4811
|
+
const signature = await wallet.signMessage(new TextEncoder().encode(message));
|
|
4812
|
+
if (!(signature instanceof Uint8Array) || signature.length !== 64) {
|
|
4813
|
+
throw new Error(
|
|
4814
|
+
`expected 64-byte Ed25519 signature, got ${signature?.length ?? "non-Uint8Array"}`
|
|
4815
|
+
);
|
|
4816
|
+
}
|
|
4817
|
+
const ikm = await crypto.subtle.importKey(
|
|
4818
|
+
"raw",
|
|
4819
|
+
signature,
|
|
4820
|
+
"HKDF",
|
|
4821
|
+
false,
|
|
4822
|
+
["deriveBits"]
|
|
4823
|
+
);
|
|
4824
|
+
const bits = await crypto.subtle.deriveBits(
|
|
4825
|
+
{
|
|
4826
|
+
name: "HKDF",
|
|
4827
|
+
hash: "SHA-256",
|
|
4828
|
+
salt: wallet.publicKey.toBytes(),
|
|
4829
|
+
info: new TextEncoder().encode("entros-protocol/identity-baseline/v1")
|
|
4830
|
+
},
|
|
4831
|
+
ikm,
|
|
4832
|
+
256
|
|
4833
|
+
);
|
|
4834
|
+
return crypto.subtle.importKey(
|
|
4835
|
+
"raw",
|
|
4836
|
+
bits,
|
|
4837
|
+
{ name: "AES-GCM" },
|
|
4838
|
+
false,
|
|
4839
|
+
["encrypt", "decrypt"]
|
|
4840
|
+
);
|
|
4841
|
+
}
|
|
4842
|
+
var baselineKeyCache = /* @__PURE__ */ new Map();
|
|
4843
|
+
async function getOrDeriveBaselineKey(wallet) {
|
|
4844
|
+
const pubkeyBase58 = wallet.publicKey.toBase58();
|
|
4845
|
+
const cached = baselineKeyCache.get(pubkeyBase58);
|
|
4846
|
+
if (cached) return cached;
|
|
4847
|
+
const pending = (async () => {
|
|
4848
|
+
try {
|
|
4849
|
+
return await deriveBaselineKey(wallet);
|
|
4850
|
+
} catch (err) {
|
|
4851
|
+
baselineKeyCache.delete(pubkeyBase58);
|
|
4852
|
+
throw err;
|
|
4853
|
+
}
|
|
4854
|
+
})();
|
|
4855
|
+
baselineKeyCache.set(pubkeyBase58, pending);
|
|
4856
|
+
return pending;
|
|
4857
|
+
}
|
|
4858
|
+
function clearBaselineKeyCache() {
|
|
4859
|
+
baselineKeyCache.clear();
|
|
4860
|
+
}
|
|
4861
|
+
function buildAAD(walletPubkey, baselinePda, commitment) {
|
|
4862
|
+
if (commitment.length !== 32) {
|
|
4863
|
+
throw new Error(`commitment must be 32 bytes, got ${commitment.length}`);
|
|
4864
|
+
}
|
|
4865
|
+
const aad = new Uint8Array(98);
|
|
4866
|
+
aad[0] = BLOB_VERSION;
|
|
4867
|
+
aad[1] = ALGORITHM_AES_256_GCM;
|
|
4868
|
+
aad.set(walletPubkey.toBytes(), 2);
|
|
4869
|
+
aad.set(baselinePda.toBytes(), 34);
|
|
4870
|
+
aad.set(commitment, 66);
|
|
4871
|
+
return aad;
|
|
4872
|
+
}
|
|
4873
|
+
async function encryptBaselineBlob(simhash2, salt, key, walletPubkey, baselinePda, commitment) {
|
|
4874
|
+
if (simhash2.length !== 32) {
|
|
4875
|
+
throw new Error(`simhash must be 32 bytes, got ${simhash2.length}`);
|
|
4876
|
+
}
|
|
4877
|
+
if (salt.length !== 32) {
|
|
4878
|
+
throw new Error(`salt must be 32 bytes, got ${salt.length}`);
|
|
4879
|
+
}
|
|
4880
|
+
const plaintext = new Uint8Array(PLAINTEXT_BYTES);
|
|
4881
|
+
plaintext.set(simhash2, 0);
|
|
4882
|
+
plaintext.set(salt, 32);
|
|
4883
|
+
const iv = crypto.getRandomValues(new Uint8Array(IV_BYTES));
|
|
4884
|
+
const aad = buildAAD(walletPubkey, baselinePda, commitment);
|
|
4885
|
+
const ciphertextWithTag = new Uint8Array(
|
|
4886
|
+
await crypto.subtle.encrypt(
|
|
4887
|
+
{
|
|
4888
|
+
name: "AES-GCM",
|
|
4889
|
+
iv,
|
|
4890
|
+
additionalData: aad
|
|
4891
|
+
},
|
|
4892
|
+
key,
|
|
4893
|
+
plaintext
|
|
4894
|
+
)
|
|
4895
|
+
);
|
|
4896
|
+
const blob = new Uint8Array(ENCRYPTED_BASELINE_BLOB_BYTES);
|
|
4897
|
+
blob[0] = BLOB_VERSION;
|
|
4898
|
+
blob[1] = ALGORITHM_AES_256_GCM;
|
|
4899
|
+
blob.set(iv, HEADER_BYTES);
|
|
4900
|
+
blob.set(ciphertextWithTag, HEADER_BYTES + IV_BYTES);
|
|
4901
|
+
return blob;
|
|
4902
|
+
}
|
|
4903
|
+
var StaleEncryptedBaselineError = class extends Error {
|
|
4904
|
+
constructor(message) {
|
|
4905
|
+
super(message);
|
|
4906
|
+
this.name = "StaleEncryptedBaselineError";
|
|
4907
|
+
}
|
|
4908
|
+
};
|
|
4909
|
+
async function decryptBaselineBlob(blob, key, walletPubkey, baselinePda, commitment) {
|
|
4910
|
+
if (commitment.length !== 32) {
|
|
4911
|
+
throw new Error(`commitment must be 32 bytes, got ${commitment.length}`);
|
|
4912
|
+
}
|
|
4913
|
+
if (blob.length !== ENCRYPTED_BASELINE_BLOB_BYTES) {
|
|
4914
|
+
throw new Error(
|
|
4915
|
+
`blob must be ${ENCRYPTED_BASELINE_BLOB_BYTES} bytes, got ${blob.length}`
|
|
4916
|
+
);
|
|
4917
|
+
}
|
|
4918
|
+
if (blob[0] !== BLOB_VERSION) {
|
|
4919
|
+
throw new Error(`unsupported blob version: ${blob[0]}`);
|
|
4920
|
+
}
|
|
4921
|
+
if (blob[1] !== ALGORITHM_AES_256_GCM) {
|
|
4922
|
+
throw new Error(`unsupported algorithm id: ${blob[1]}`);
|
|
4923
|
+
}
|
|
4924
|
+
const iv = blob.slice(HEADER_BYTES, HEADER_BYTES + IV_BYTES);
|
|
4925
|
+
const ciphertextWithTag = blob.slice(
|
|
4926
|
+
HEADER_BYTES + IV_BYTES,
|
|
4927
|
+
ENCRYPTED_BASELINE_BLOB_BYTES
|
|
4928
|
+
);
|
|
4929
|
+
const aad = buildAAD(walletPubkey, baselinePda, commitment);
|
|
4930
|
+
let plaintext;
|
|
4931
|
+
try {
|
|
4932
|
+
plaintext = await crypto.subtle.decrypt(
|
|
4933
|
+
{
|
|
4934
|
+
name: "AES-GCM",
|
|
4935
|
+
iv,
|
|
4936
|
+
additionalData: aad
|
|
4937
|
+
},
|
|
4938
|
+
key,
|
|
4939
|
+
ciphertextWithTag
|
|
4940
|
+
);
|
|
4941
|
+
} catch (err) {
|
|
4942
|
+
if (err instanceof Error && err.name === "OperationError") {
|
|
4943
|
+
throw new StaleEncryptedBaselineError(
|
|
4944
|
+
"encrypted baseline auth-tag verification failed \u2014 blob is stale or AAD does not match"
|
|
4945
|
+
);
|
|
4946
|
+
}
|
|
4947
|
+
throw err;
|
|
4948
|
+
}
|
|
4949
|
+
const bytes = new Uint8Array(plaintext);
|
|
4950
|
+
return {
|
|
4951
|
+
simhash: bytes.slice(0, 32),
|
|
4952
|
+
salt: bytes.slice(32, 64)
|
|
4953
|
+
};
|
|
4954
|
+
}
|
|
4955
|
+
async function fetchEncryptedBaseline(walletPubkey, connection) {
|
|
4956
|
+
const [baselinePda] = await deriveEncryptedBaselinePda(walletPubkey);
|
|
4957
|
+
const accountInfo = await connection.getAccountInfo(baselinePda);
|
|
4958
|
+
if (!accountInfo) return null;
|
|
4959
|
+
const raw = accountInfo.data instanceof Uint8Array ? accountInfo.data : new Uint8Array(accountInfo.data);
|
|
4960
|
+
if (raw.length < 8 + ENCRYPTED_BASELINE_BLOB_BYTES + 1) {
|
|
4961
|
+
return null;
|
|
4962
|
+
}
|
|
4963
|
+
return raw.slice(8, 8 + ENCRYPTED_BASELINE_BLOB_BYTES);
|
|
4964
|
+
}
|
|
4965
|
+
|
|
4524
4966
|
// src/submit/wallet.ts
|
|
4967
|
+
async function buildSetEncryptedBaselineIx(anchorProgram, walletPubkey, blob) {
|
|
4968
|
+
if (blob.length !== ENCRYPTED_BASELINE_BLOB_BYTES) {
|
|
4969
|
+
throw new Error(
|
|
4970
|
+
`encrypted baseline blob must be ${ENCRYPTED_BASELINE_BLOB_BYTES} bytes, got ${blob.length}`
|
|
4971
|
+
);
|
|
4972
|
+
}
|
|
4973
|
+
const { PublicKey, SystemProgram } = await import("@solana/web3.js");
|
|
4974
|
+
const programId = new PublicKey(PROGRAM_IDS.entrosAnchor);
|
|
4975
|
+
const [identityPda] = PublicKey.findProgramAddressSync(
|
|
4976
|
+
[new TextEncoder().encode("identity"), walletPubkey.toBuffer()],
|
|
4977
|
+
programId
|
|
4978
|
+
);
|
|
4979
|
+
const [encryptedBaselinePda] = PublicKey.findProgramAddressSync(
|
|
4980
|
+
[new TextEncoder().encode("encrypted_baseline"), walletPubkey.toBuffer()],
|
|
4981
|
+
programId
|
|
4982
|
+
);
|
|
4983
|
+
return anchorProgram.methods.setEncryptedBaseline(Array.from(blob)).accounts({
|
|
4984
|
+
authority: walletPubkey,
|
|
4985
|
+
identityState: identityPda,
|
|
4986
|
+
encryptedBaseline: encryptedBaselinePda,
|
|
4987
|
+
systemProgram: SystemProgram.programId
|
|
4988
|
+
}).instruction();
|
|
4989
|
+
}
|
|
4525
4990
|
async function confirmAndCheck(connection, signature) {
|
|
4526
4991
|
if (!signature) {
|
|
4527
4992
|
throw new Error("confirmAndCheck called without a transaction signature");
|
|
@@ -4708,10 +5173,19 @@ async function submitViaWallet(proof, commitment, options) {
|
|
|
4708
5173
|
systemProgram: SystemProgram.programId
|
|
4709
5174
|
}).instruction();
|
|
4710
5175
|
const tx = new Transaction();
|
|
4711
|
-
|
|
5176
|
+
const computeUnitLimit = options.encryptedBaselineBlob ? 3e5 : 25e4;
|
|
5177
|
+
tx.add(ComputeBudgetProgram.setComputeUnitLimit({ units: computeUnitLimit }));
|
|
4712
5178
|
tx.add(createChallengeIx);
|
|
4713
5179
|
tx.add(verifyProofIx);
|
|
4714
5180
|
tx.add(updateAnchorIx);
|
|
5181
|
+
if (options.encryptedBaselineBlob) {
|
|
5182
|
+
const setBaselineIx = await buildSetEncryptedBaselineIx(
|
|
5183
|
+
anchorProgram,
|
|
5184
|
+
provider.wallet.publicKey,
|
|
5185
|
+
options.encryptedBaselineBlob
|
|
5186
|
+
);
|
|
5187
|
+
tx.add(setBaselineIx);
|
|
5188
|
+
}
|
|
4715
5189
|
tx.feePayer = provider.wallet.publicKey;
|
|
4716
5190
|
tx.recentBlockhash = (await options.connection.getLatestBlockhash("confirmed")).blockhash;
|
|
4717
5191
|
txSig = await options.wallet.sendTransaction(tx, options.connection, {
|
|
@@ -4787,9 +5261,18 @@ async function submitViaWallet(proof, commitment, options) {
|
|
|
4787
5261
|
);
|
|
4788
5262
|
}
|
|
4789
5263
|
const tx = new Transaction();
|
|
4790
|
-
|
|
5264
|
+
const computeUnitLimit = options.encryptedBaselineBlob ? 25e4 : 2e5;
|
|
5265
|
+
tx.add(ComputeBudgetProgram.setComputeUnitLimit({ units: computeUnitLimit }));
|
|
4791
5266
|
if (ed25519Ix) tx.add(ed25519Ix);
|
|
4792
5267
|
tx.add(mintAnchorIx);
|
|
5268
|
+
if (options.encryptedBaselineBlob) {
|
|
5269
|
+
const setBaselineIx = await buildSetEncryptedBaselineIx(
|
|
5270
|
+
anchorProgram,
|
|
5271
|
+
provider.wallet.publicKey,
|
|
5272
|
+
options.encryptedBaselineBlob
|
|
5273
|
+
);
|
|
5274
|
+
tx.add(setBaselineIx);
|
|
5275
|
+
}
|
|
4793
5276
|
tx.feePayer = provider.wallet.publicKey;
|
|
4794
5277
|
tx.recentBlockhash = (await options.connection.getLatestBlockhash("confirmed")).blockhash;
|
|
4795
5278
|
txSig = await options.wallet.sendTransaction(tx, options.connection, {
|
|
@@ -4844,8 +5327,16 @@ async function submitResetViaWallet(commitment, options) {
|
|
|
4844
5327
|
systemProgram: SystemProgram.programId
|
|
4845
5328
|
}).instruction();
|
|
4846
5329
|
const tx = new Transaction();
|
|
4847
|
-
tx.add(ComputeBudgetProgram.setComputeUnitLimit({ units:
|
|
5330
|
+
tx.add(ComputeBudgetProgram.setComputeUnitLimit({ units: 2e5 }));
|
|
4848
5331
|
tx.add(resetIx);
|
|
5332
|
+
if (options.encryptedBaselineBlob) {
|
|
5333
|
+
const setBaselineIx = await buildSetEncryptedBaselineIx(
|
|
5334
|
+
anchorProgram,
|
|
5335
|
+
provider.wallet.publicKey,
|
|
5336
|
+
options.encryptedBaselineBlob
|
|
5337
|
+
);
|
|
5338
|
+
tx.add(setBaselineIx);
|
|
5339
|
+
}
|
|
4849
5340
|
tx.feePayer = provider.wallet.publicKey;
|
|
4850
5341
|
tx.recentBlockhash = (await options.connection.getLatestBlockhash("confirmed")).blockhash;
|
|
4851
5342
|
const txSig = await options.wallet.sendTransaction(
|
|
@@ -5064,19 +5555,19 @@ async function fetchIdentityState(walletPubkey, connection) {
|
|
|
5064
5555
|
const accountInfo = await connection.getAccountInfo(identityPda);
|
|
5065
5556
|
if (!accountInfo) return null;
|
|
5066
5557
|
const coder = new anchor.BorshAccountsCoder(entros_anchor_default);
|
|
5067
|
-
const decoded = coder.decode("
|
|
5558
|
+
const decoded = coder.decode("IdentityState", accountInfo.data);
|
|
5068
5559
|
return {
|
|
5069
5560
|
owner: decoded.owner.toBase58(),
|
|
5070
|
-
creationTimestamp: decoded.
|
|
5071
|
-
lastVerificationTimestamp: decoded.
|
|
5072
|
-
verificationCount: decoded.
|
|
5073
|
-
trustScore: decoded.
|
|
5074
|
-
currentCommitment: new Uint8Array(decoded.
|
|
5561
|
+
creationTimestamp: decoded.creation_timestamp.toNumber(),
|
|
5562
|
+
lastVerificationTimestamp: decoded.last_verification_timestamp.toNumber(),
|
|
5563
|
+
verificationCount: decoded.verification_count,
|
|
5564
|
+
trustScore: decoded.trust_score,
|
|
5565
|
+
currentCommitment: new Uint8Array(decoded.current_commitment),
|
|
5075
5566
|
mint: decoded.mint.toBase58(),
|
|
5076
5567
|
// Anchor's Borsh coder returns the raw BN for i64 fields; .toNumber()
|
|
5077
5568
|
// is safe here because Unix timestamps fit in Number.MAX_SAFE_INTEGER
|
|
5078
5569
|
// until year 275760.
|
|
5079
|
-
lastResetTimestamp: decoded.
|
|
5570
|
+
lastResetTimestamp: decoded.last_reset_timestamp?.toNumber?.() ?? 0
|
|
5080
5571
|
};
|
|
5081
5572
|
} catch {
|
|
5082
5573
|
return null;
|
|
@@ -5160,6 +5651,65 @@ async function loadVerificationData() {
|
|
|
5160
5651
|
return inMemoryStore;
|
|
5161
5652
|
}
|
|
5162
5653
|
}
|
|
5654
|
+
async function recoverBaselineFromChain(wallet, connection) {
|
|
5655
|
+
try {
|
|
5656
|
+
const identity = await fetchIdentityState(
|
|
5657
|
+
wallet.publicKey.toBase58(),
|
|
5658
|
+
connection
|
|
5659
|
+
);
|
|
5660
|
+
if (!identity) {
|
|
5661
|
+
return { recovered: false, reason: "no-on-chain-identity" };
|
|
5662
|
+
}
|
|
5663
|
+
const blob = await fetchEncryptedBaseline(wallet.publicKey, connection);
|
|
5664
|
+
if (!blob) {
|
|
5665
|
+
return { recovered: false, reason: "no-encrypted-baseline" };
|
|
5666
|
+
}
|
|
5667
|
+
let key;
|
|
5668
|
+
try {
|
|
5669
|
+
key = await getOrDeriveBaselineKey(wallet);
|
|
5670
|
+
} catch (err) {
|
|
5671
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
5672
|
+
return {
|
|
5673
|
+
recovered: false,
|
|
5674
|
+
reason: "signing-unavailable",
|
|
5675
|
+
detail
|
|
5676
|
+
};
|
|
5677
|
+
}
|
|
5678
|
+
const [baselinePda] = await deriveEncryptedBaselinePda(wallet.publicKey);
|
|
5679
|
+
let plaintext;
|
|
5680
|
+
try {
|
|
5681
|
+
plaintext = await decryptBaselineBlob(
|
|
5682
|
+
blob,
|
|
5683
|
+
key,
|
|
5684
|
+
wallet.publicKey,
|
|
5685
|
+
baselinePda,
|
|
5686
|
+
identity.currentCommitment
|
|
5687
|
+
);
|
|
5688
|
+
} catch (err) {
|
|
5689
|
+
if (err instanceof StaleEncryptedBaselineError) {
|
|
5690
|
+
return { recovered: false, reason: "stale-baseline" };
|
|
5691
|
+
}
|
|
5692
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
5693
|
+
return { recovered: false, reason: "unknown-error", detail };
|
|
5694
|
+
}
|
|
5695
|
+
const fingerprint = bytesToFingerprint(plaintext.simhash);
|
|
5696
|
+
const saltBigint = bytes32ToBigint(plaintext.salt);
|
|
5697
|
+
const commitmentBigint = bytes32ToBigint(identity.currentCommitment);
|
|
5698
|
+
await storeVerificationData({
|
|
5699
|
+
fingerprint,
|
|
5700
|
+
salt: saltBigint.toString(),
|
|
5701
|
+
commitment: commitmentBigint.toString(),
|
|
5702
|
+
timestamp: identity.lastVerificationTimestamp > 0 ? identity.lastVerificationTimestamp * 1e3 : Date.now()
|
|
5703
|
+
});
|
|
5704
|
+
sdkLog(
|
|
5705
|
+
"[Entros SDK] Recovered local baseline from on-chain EncryptedBaseline PDA"
|
|
5706
|
+
);
|
|
5707
|
+
return { recovered: true };
|
|
5708
|
+
} catch (err) {
|
|
5709
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
5710
|
+
return { recovered: false, reason: "unknown-error", detail };
|
|
5711
|
+
}
|
|
5712
|
+
}
|
|
5163
5713
|
|
|
5164
5714
|
// src/pulse.ts
|
|
5165
5715
|
async function extractFeatures(data) {
|
|
@@ -5270,6 +5820,41 @@ async function extractFingerprintAndValidate(sensorData, config, walletAddress,
|
|
|
5270
5820
|
}
|
|
5271
5821
|
return { ok: true, features, f0Contour, accelMagnitude, fingerprint, tbh, signedReceipt };
|
|
5272
5822
|
}
|
|
5823
|
+
function resolveBaselineWallet(wallet) {
|
|
5824
|
+
if (!wallet) return null;
|
|
5825
|
+
const adapter = wallet.adapter ?? wallet;
|
|
5826
|
+
if (!adapter?.publicKey || typeof adapter.signMessage !== "function") {
|
|
5827
|
+
return null;
|
|
5828
|
+
}
|
|
5829
|
+
return {
|
|
5830
|
+
publicKey: adapter.publicKey,
|
|
5831
|
+
signMessage: adapter.signMessage.bind(adapter)
|
|
5832
|
+
};
|
|
5833
|
+
}
|
|
5834
|
+
async function buildEncryptedBaselineBlobBestEffort(wallet, fingerprint, salt, commitmentBytes) {
|
|
5835
|
+
const baselineWallet = resolveBaselineWallet(wallet);
|
|
5836
|
+
if (!baselineWallet) return void 0;
|
|
5837
|
+
try {
|
|
5838
|
+
const key = await getOrDeriveBaselineKey(baselineWallet);
|
|
5839
|
+
const [baselinePda] = await deriveEncryptedBaselinePda(baselineWallet.publicKey);
|
|
5840
|
+
const simhashBytes = fingerprintToBytes(fingerprint);
|
|
5841
|
+
const saltBytes = bigintToBytes32(salt);
|
|
5842
|
+
return await encryptBaselineBlob(
|
|
5843
|
+
simhashBytes,
|
|
5844
|
+
saltBytes,
|
|
5845
|
+
key,
|
|
5846
|
+
baselineWallet.publicKey,
|
|
5847
|
+
baselinePda,
|
|
5848
|
+
commitmentBytes
|
|
5849
|
+
);
|
|
5850
|
+
} catch (err) {
|
|
5851
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5852
|
+
sdkWarn(
|
|
5853
|
+
`[Entros SDK] Encrypted-baseline build skipped (cross-device recovery unavailable this session): ${msg}`
|
|
5854
|
+
);
|
|
5855
|
+
return void 0;
|
|
5856
|
+
}
|
|
5857
|
+
}
|
|
5273
5858
|
async function processSensorData(sensorData, config, wallet, connection, onProgress) {
|
|
5274
5859
|
const audioSamples = sensorData.audio?.samples.length ?? 0;
|
|
5275
5860
|
const motionSamples = sensorData.motion.length;
|
|
@@ -5341,7 +5926,7 @@ async function processSensorData(sensorData, config, wallet, connection, onProgr
|
|
|
5341
5926
|
}
|
|
5342
5927
|
const { fingerprint, tbh, features, signedReceipt } = extraction;
|
|
5343
5928
|
let isFirstVerification;
|
|
5344
|
-
|
|
5929
|
+
let previousData = await loadVerificationData();
|
|
5345
5930
|
if (wallet && connection) {
|
|
5346
5931
|
const walletPubkey = wallet.adapter?.publicKey ?? wallet.publicKey;
|
|
5347
5932
|
if (walletPubkey) {
|
|
@@ -5363,6 +5948,21 @@ async function processSensorData(sensorData, config, wallet, connection, onProgr
|
|
|
5363
5948
|
} else {
|
|
5364
5949
|
isFirstVerification = !previousData;
|
|
5365
5950
|
}
|
|
5951
|
+
if (!isFirstVerification && !previousData && wallet && connection) {
|
|
5952
|
+
const baselineWallet = resolveBaselineWallet(wallet);
|
|
5953
|
+
if (baselineWallet) {
|
|
5954
|
+
onProgress?.("Recovering baseline from chain...");
|
|
5955
|
+
const recovery = await recoverBaselineFromChain(baselineWallet, connection);
|
|
5956
|
+
if (recovery.recovered) {
|
|
5957
|
+
previousData = await loadVerificationData();
|
|
5958
|
+
sdkLog("[Entros SDK] On-chain encrypted baseline recovered");
|
|
5959
|
+
} else {
|
|
5960
|
+
sdkLog(
|
|
5961
|
+
`[Entros SDK] On-chain encrypted baseline recovery not available (${recovery.reason ?? "unknown"})`
|
|
5962
|
+
);
|
|
5963
|
+
}
|
|
5964
|
+
}
|
|
5965
|
+
}
|
|
5366
5966
|
if (!isFirstVerification && !previousData) {
|
|
5367
5967
|
return {
|
|
5368
5968
|
success: false,
|
|
@@ -5428,6 +6028,12 @@ async function processSensorData(sensorData, config, wallet, connection, onProgr
|
|
|
5428
6028
|
onProgress?.("Submitting to Solana...");
|
|
5429
6029
|
let submission;
|
|
5430
6030
|
if (wallet && connection) {
|
|
6031
|
+
const encryptedBaselineBlob = await buildEncryptedBaselineBlobBestEffort(
|
|
6032
|
+
wallet,
|
|
6033
|
+
tbh.fingerprint,
|
|
6034
|
+
tbh.salt,
|
|
6035
|
+
tbh.commitmentBytes
|
|
6036
|
+
);
|
|
5431
6037
|
if (isFirstVerification) {
|
|
5432
6038
|
submission = await submitViaWallet(
|
|
5433
6039
|
solanaProof ?? { proofBytes: new Uint8Array(0), publicInputs: [] },
|
|
@@ -5438,7 +6044,8 @@ async function processSensorData(sensorData, config, wallet, connection, onProgr
|
|
|
5438
6044
|
isFirstVerification: true,
|
|
5439
6045
|
relayerUrl: config.relayerUrl,
|
|
5440
6046
|
relayerApiKey: config.relayerApiKey,
|
|
5441
|
-
signedReceipt
|
|
6047
|
+
signedReceipt,
|
|
6048
|
+
encryptedBaselineBlob
|
|
5442
6049
|
}
|
|
5443
6050
|
);
|
|
5444
6051
|
} else {
|
|
@@ -5447,7 +6054,8 @@ async function processSensorData(sensorData, config, wallet, connection, onProgr
|
|
|
5447
6054
|
connection,
|
|
5448
6055
|
isFirstVerification: false,
|
|
5449
6056
|
relayerUrl: config.relayerUrl,
|
|
5450
|
-
relayerApiKey: config.relayerApiKey
|
|
6057
|
+
relayerApiKey: config.relayerApiKey,
|
|
6058
|
+
encryptedBaselineBlob
|
|
5451
6059
|
});
|
|
5452
6060
|
}
|
|
5453
6061
|
} else if (config.relayerUrl) {
|
|
@@ -5529,12 +6137,19 @@ async function processResetSensorData(sensorData, config, wallet, connection, on
|
|
|
5529
6137
|
};
|
|
5530
6138
|
}
|
|
5531
6139
|
const { tbh } = extraction;
|
|
6140
|
+
const encryptedBaselineBlob = await buildEncryptedBaselineBlobBestEffort(
|
|
6141
|
+
wallet,
|
|
6142
|
+
tbh.fingerprint,
|
|
6143
|
+
tbh.salt,
|
|
6144
|
+
tbh.commitmentBytes
|
|
6145
|
+
);
|
|
5532
6146
|
onProgress?.("Submitting reset to Solana...");
|
|
5533
6147
|
const submission = await submitResetViaWallet(tbh.commitmentBytes, {
|
|
5534
6148
|
wallet,
|
|
5535
6149
|
connection,
|
|
5536
6150
|
relayerUrl: config.relayerUrl,
|
|
5537
|
-
relayerApiKey: config.relayerApiKey
|
|
6151
|
+
relayerApiKey: config.relayerApiKey,
|
|
6152
|
+
encryptedBaselineBlob
|
|
5538
6153
|
});
|
|
5539
6154
|
if (submission.success) {
|
|
5540
6155
|
try {
|
|
@@ -6422,6 +7037,7 @@ async function fetchChallenge(executorUrl, walletAddress, apiKey) {
|
|
|
6422
7037
|
DEFAULT_CAPTURE_MS,
|
|
6423
7038
|
DEFAULT_MIN_DISTANCE,
|
|
6424
7039
|
DEFAULT_THRESHOLD,
|
|
7040
|
+
ENCRYPTED_BASELINE_BLOB_BYTES,
|
|
6425
7041
|
FINGERPRINT_BITS,
|
|
6426
7042
|
MAX_CAPTURE_MS,
|
|
6427
7043
|
MIN_AUDIO_SAMPLES,
|
|
@@ -6433,11 +7049,19 @@ async function fetchChallenge(executorUrl, walletAddress, apiKey) {
|
|
|
6433
7049
|
PulseSDK,
|
|
6434
7050
|
PulseSession,
|
|
6435
7051
|
SPEAKER_FEATURE_COUNT,
|
|
7052
|
+
StaleEncryptedBaselineError,
|
|
6436
7053
|
TOUCH_FEATURE_COUNT,
|
|
6437
7054
|
attestAgentOperator,
|
|
6438
7055
|
bigintToBytes32,
|
|
7056
|
+
bytes32ToBigint,
|
|
7057
|
+
bytesToFingerprint,
|
|
7058
|
+
clearBaselineKeyCache,
|
|
6439
7059
|
computeCommitment,
|
|
7060
|
+
decryptBaselineBlob,
|
|
7061
|
+
deriveBaselineKey,
|
|
7062
|
+
deriveEncryptedBaselinePda,
|
|
6440
7063
|
encodeAudioAsBase64,
|
|
7064
|
+
encryptBaselineBlob,
|
|
6441
7065
|
extractAccelerationMagnitude,
|
|
6442
7066
|
extractMotionFeatures,
|
|
6443
7067
|
extractMouseDynamics,
|
|
@@ -6445,7 +7069,9 @@ async function fetchChallenge(executorUrl, walletAddress, apiKey) {
|
|
|
6445
7069
|
extractSpeakerFeaturesDetailed,
|
|
6446
7070
|
extractTouchFeatures,
|
|
6447
7071
|
fetchChallenge,
|
|
7072
|
+
fetchEncryptedBaseline,
|
|
6448
7073
|
fetchIdentityState,
|
|
7074
|
+
fingerprintToBytes,
|
|
6449
7075
|
fuseFeatures,
|
|
6450
7076
|
fuseRawFeatures,
|
|
6451
7077
|
generateLissajousPoints,
|
|
@@ -6457,11 +7083,13 @@ async function fetchChallenge(executorUrl, walletAddress, apiKey) {
|
|
|
6457
7083
|
generateSolanaProof,
|
|
6458
7084
|
generateTBH,
|
|
6459
7085
|
getAgentHumanOperator,
|
|
7086
|
+
getOrDeriveBaselineKey,
|
|
6460
7087
|
hammingDistance,
|
|
6461
7088
|
loadVerificationData,
|
|
6462
7089
|
packBits,
|
|
6463
7090
|
prepareCircuitInput,
|
|
6464
7091
|
randomLissajousParams,
|
|
7092
|
+
recoverBaselineFromChain,
|
|
6465
7093
|
serializeProof,
|
|
6466
7094
|
simhash,
|
|
6467
7095
|
storeVerificationData,
|