@qrlwallet/connect 3.3.0 → 4.0.0
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/README.md +115 -52
- package/dist/index.d.mts +248 -69
- package/dist/index.d.ts +248 -69
- package/dist/index.js +2897 -1159
- package/dist/index.mjs +2873 -1158
- package/package.json +8 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import EventEmitter from 'eventemitter3';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Post-quantum handshake (
|
|
4
|
+
* Post-quantum handshake (v3).
|
|
5
5
|
*
|
|
6
6
|
* Flow:
|
|
7
7
|
* dApp relay wallet
|
|
8
8
|
* ────────────────────────────────────────────────────────────────────
|
|
9
|
-
* initiate() → (pk, sk)
|
|
10
|
-
* QR(cid,
|
|
11
|
-
* receiveQR(cid, pk)
|
|
9
|
+
* initiate() → (pk, sk, cap)
|
|
10
|
+
* QR(cid, fp, cap) rendered scan QR
|
|
11
|
+
* receiveQR(cid, pk, cap)
|
|
12
12
|
* → SYNACK{ct, c0}
|
|
13
13
|
* ◄── SYNACK ─────── send
|
|
14
14
|
* onSynAck(cid, SYNACK)
|
|
@@ -20,6 +20,7 @@ import EventEmitter from 'eventemitter3';
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
interface Session {
|
|
23
|
+
protocolVersion: 3;
|
|
23
24
|
cid: Uint8Array;
|
|
24
25
|
key: CryptoKey;
|
|
25
26
|
htx: Uint8Array;
|
|
@@ -30,6 +31,7 @@ interface Session {
|
|
|
30
31
|
}
|
|
31
32
|
/** Wire-safe persisted form for localStorage. */
|
|
32
33
|
interface PersistedSession {
|
|
34
|
+
protocolVersion: 3;
|
|
33
35
|
cid: string;
|
|
34
36
|
kAeadRaw: string;
|
|
35
37
|
htx: string;
|
|
@@ -49,6 +51,10 @@ interface AckMessage {
|
|
|
49
51
|
c1: string;
|
|
50
52
|
v: number;
|
|
51
53
|
}
|
|
54
|
+
interface InitiatedKeyExchange {
|
|
55
|
+
publicKey: Uint8Array;
|
|
56
|
+
capability: Uint8Array;
|
|
57
|
+
}
|
|
52
58
|
interface KeyExchangeEvents {
|
|
53
59
|
keys_exchanged: () => void;
|
|
54
60
|
step_change: (step: KeyExchangeMessageType) => void;
|
|
@@ -56,29 +62,40 @@ interface KeyExchangeEvents {
|
|
|
56
62
|
declare class KeyExchange extends EventEmitter<KeyExchangeEvents> {
|
|
57
63
|
private isOriginator;
|
|
58
64
|
private keypair;
|
|
65
|
+
private capability;
|
|
59
66
|
private session;
|
|
60
67
|
private step;
|
|
61
68
|
private awaitingSynAck;
|
|
62
69
|
private awaitingAck;
|
|
70
|
+
private awaitingAckDelivery;
|
|
63
71
|
private keysExchanged;
|
|
72
|
+
private stateGeneration;
|
|
64
73
|
private lastAck;
|
|
65
74
|
constructor(isOriginator: boolean, restored?: Session);
|
|
66
75
|
/**
|
|
67
76
|
* Originator (dApp) side: begin a fresh handshake by generating an
|
|
68
|
-
* ephemeral ML-KEM-768 keypair
|
|
77
|
+
* ephemeral ML-KEM-768 keypair and a fresh 32-byte pairing capability.
|
|
78
|
+
* Both returned arrays are copies; the originator retains its own secret
|
|
79
|
+
* capability until the handshake succeeds or is retired.
|
|
69
80
|
*/
|
|
70
|
-
initiate():
|
|
81
|
+
initiate(): InitiatedKeyExchange;
|
|
71
82
|
/**
|
|
72
83
|
* Originator: handle a SYNACK received over the relay.
|
|
73
84
|
* Returns an ACK wire message to send, or null if the SYNACK was a
|
|
74
85
|
* duplicate (handshake already complete).
|
|
75
86
|
*/
|
|
76
87
|
onSynAck(cid: Uint8Array, msg: SynAckMessage): Promise<AckMessage | null>;
|
|
88
|
+
/**
|
|
89
|
+
* Originator: publish completion only after the relay acknowledged ACK.
|
|
90
|
+
* This is deliberately separate from onSynAck() so transport ambiguity
|
|
91
|
+
* cannot leave a live or persistable one-sided session.
|
|
92
|
+
*/
|
|
93
|
+
confirmOriginatorAckDelivered(): void;
|
|
77
94
|
/**
|
|
78
95
|
* Responder (wallet) side: begin handshake from a scanned QR.
|
|
79
96
|
* Returns the SYNACK wire message to emit on the relay.
|
|
80
97
|
*/
|
|
81
|
-
receiveQR(cid: Uint8Array, pk: Uint8Array): Promise<SynAckMessage>;
|
|
98
|
+
receiveQR(cid: Uint8Array, pk: Uint8Array, capability: Uint8Array): Promise<SynAckMessage>;
|
|
82
99
|
/**
|
|
83
100
|
* Responder: verify an incoming ACK and finalize the session.
|
|
84
101
|
* Duplicates are silently ignored (handshake is idempotent).
|
|
@@ -108,10 +125,11 @@ interface DAppMetadata {
|
|
|
108
125
|
url: string;
|
|
109
126
|
icon?: string;
|
|
110
127
|
/**
|
|
111
|
-
* Optional "return to dApp" target
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
* so a same-device
|
|
128
|
+
* Optional credential-free HTTP(S) "return to dApp" target. HTTPS is
|
|
129
|
+
* required except for explicit loopback development URLs. Sent to the
|
|
130
|
+
* wallet in ORIGINATOR_INFO; after the wallet resolves a restricted request
|
|
131
|
+
* it bounces the user back here so a same-device flow does not strand the
|
|
132
|
+
* user in the wallet.
|
|
115
133
|
*/
|
|
116
134
|
redirectUrl?: string;
|
|
117
135
|
}
|
|
@@ -122,11 +140,12 @@ interface DAppMetadata {
|
|
|
122
140
|
* the ML-KEM keypair is ephemeral and zeroized after the handshake.
|
|
123
141
|
* Re-pair (generate a new QR) to rotate the session key.
|
|
124
142
|
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
143
|
+
* v5 checkpoints every seal/open, requires exclusive browser-tab ownership,
|
|
144
|
+
* and proves the AEAD key came from a PQP3 capability-bound handshake.
|
|
145
|
+
* Older records cannot prove all three invariants and are dropped.
|
|
127
146
|
*/
|
|
128
147
|
interface DAppSession {
|
|
129
|
-
version:
|
|
148
|
+
version: 5;
|
|
130
149
|
channelId: string;
|
|
131
150
|
keyExchange: PersistedSession;
|
|
132
151
|
dappMetadata: DAppMetadata;
|
|
@@ -140,6 +159,8 @@ interface PendingRequest {
|
|
|
140
159
|
id: string | number;
|
|
141
160
|
method: string;
|
|
142
161
|
params?: unknown[] | undefined;
|
|
162
|
+
/** Immutable postcondition captured before caller-owned params can change. */
|
|
163
|
+
expectedChainId?: string | undefined;
|
|
143
164
|
resolve: (result: unknown) => void;
|
|
144
165
|
reject: (error: Error) => void;
|
|
145
166
|
timestamp: number;
|
|
@@ -147,9 +168,10 @@ interface PendingRequest {
|
|
|
147
168
|
/**
|
|
148
169
|
* Key-exchange wire-message types.
|
|
149
170
|
*
|
|
150
|
-
*
|
|
151
|
-
* over the relay. The SYN step is
|
|
152
|
-
*
|
|
171
|
+
* v3 note: only SYNACK (wallet to dApp) and ACK (dApp to wallet) are
|
|
172
|
+
* transmitted over the relay. The SYN step is represented by the pairing URI,
|
|
173
|
+
* while the relay carries only the dApp's ML-KEM public key. The URI contains
|
|
174
|
+
* the key fingerprint and a capability that is never uploaded to the relay.
|
|
153
175
|
*/
|
|
154
176
|
declare enum KeyExchangeMessageType {
|
|
155
177
|
SYN = "key_handshake_SYN",
|
|
@@ -250,28 +272,65 @@ interface EIP6963ProviderInfoOverride {
|
|
|
250
272
|
* Rich response object returned by both `qrl_signMessage` and
|
|
251
273
|
* `qrl_signTypedData`. The signature alone is not enough to verify since
|
|
252
274
|
* ML-DSA-87 public keys cannot be recovered from a signature, so the
|
|
253
|
-
* wallet always returns the public key explicitly. Stateless verifiers
|
|
254
|
-
*
|
|
275
|
+
* wallet always returns the public key explicitly. Stateless verifiers need
|
|
276
|
+
* the original payload plus the signature fields below.
|
|
255
277
|
*/
|
|
256
278
|
interface QrlSignedResult {
|
|
257
|
-
/** 0x-hex of the
|
|
279
|
+
/** 0x-hex of the 4627-byte ML-DSA-87 signature. */
|
|
258
280
|
signature: string;
|
|
259
281
|
/** 0x-hex of the 2592-byte ML-DSA-87 public key. */
|
|
260
282
|
publicKey: string;
|
|
261
|
-
/**
|
|
283
|
+
/**
|
|
284
|
+
* 0x-hex of the 3-byte wallet descriptor used to derive `signer`.
|
|
285
|
+
* Optional while older wallet builds are upgraded; bound verification
|
|
286
|
+
* requires it.
|
|
287
|
+
*/
|
|
288
|
+
descriptor?: string;
|
|
289
|
+
/** Current 41-character checksummed Q-address derived from descriptor + key. */
|
|
262
290
|
signer: string;
|
|
263
291
|
/** 0x-hex of the 64-byte SHAKE256 digest that was signed. */
|
|
264
292
|
digest: string;
|
|
265
293
|
/** Scheme tag: 'QRL-SIGN-MSG-v1' or 'QRL-SIGN-TYPED-v1'. */
|
|
266
|
-
schemeVersion:
|
|
294
|
+
schemeVersion: QrlSigningSchemeVersion;
|
|
295
|
+
}
|
|
296
|
+
/** A current wallet response whose descriptor can be signer-bound. */
|
|
297
|
+
interface QrlSignedResultWithDescriptor extends QrlSignedResult {
|
|
298
|
+
descriptor: string;
|
|
299
|
+
}
|
|
300
|
+
type QrlSigningSchemeVersion = 'QRL-SIGN-MSG-v1' | 'QRL-SIGN-TYPED-v1';
|
|
301
|
+
/** Strict wire shape returned by `qrl_signMessage`. */
|
|
302
|
+
interface QrlSignedMessageResult extends QrlSignedResult {
|
|
303
|
+
schemeVersion: 'QRL-SIGN-MSG-v1';
|
|
267
304
|
}
|
|
268
305
|
/**
|
|
269
306
|
* Response from `qrl_signTypedData`. Echoes `domain` so a stateless
|
|
270
307
|
* verifier doesn't need to be told the domain out-of-band.
|
|
271
308
|
*/
|
|
272
309
|
interface QrlSignedTypedDataResult extends QrlSignedResult {
|
|
310
|
+
schemeVersion: 'QRL-SIGN-TYPED-v1';
|
|
273
311
|
domain: Record<string, unknown>;
|
|
274
312
|
}
|
|
313
|
+
/** Every strict signing-result wire shape understood by this SDK. */
|
|
314
|
+
type QrlSigningResult = QrlSignedMessageResult | QrlSignedTypedDataResult;
|
|
315
|
+
/**
|
|
316
|
+
* Validate an unknown value as the exact `qrl_signMessage` result wire shape.
|
|
317
|
+
* This checks structure and fixed widths only. Use `verifyMessageForSigner`
|
|
318
|
+
* with the original message to authenticate the signature and signer binding.
|
|
319
|
+
*/
|
|
320
|
+
declare function isQrlSignedMessageResult(value: unknown): value is QrlSignedMessageResult;
|
|
321
|
+
/**
|
|
322
|
+
* Validate an unknown value as the exact `qrl_signTypedData` result wire shape.
|
|
323
|
+
* This checks structure and fixed widths only. Use `verifyTypedDataForSigner`
|
|
324
|
+
* with the original payload to authenticate the signature and signer binding.
|
|
325
|
+
*/
|
|
326
|
+
declare function isQrlSignedTypedDataResult(value: unknown): value is QrlSignedTypedDataResult;
|
|
327
|
+
/** Validate either exact signing-result wire shape from an unknown value. */
|
|
328
|
+
declare function isQrlSignedResult(value: unknown): value is QrlSigningResult;
|
|
329
|
+
/**
|
|
330
|
+
* Strictly validate an unknown signing result and narrow it to one carrying
|
|
331
|
+
* the exact 3-byte ML-DSA descriptor required for signer-bound verification.
|
|
332
|
+
*/
|
|
333
|
+
declare function hasSigningDescriptor(result: unknown): result is QrlSigningResult & QrlSignedResultWithDescriptor;
|
|
275
334
|
/**
|
|
276
335
|
* `qrl_signMessage` params: `[signer, messageHex]`. `signer` must equal the
|
|
277
336
|
* dApp's currently-connected account; the wallet rejects mismatches before
|
|
@@ -353,6 +412,12 @@ declare class QRLConnectProvider extends EventEmitter<ProviderEvents> {
|
|
|
353
412
|
private orphanedRequests;
|
|
354
413
|
private readonly requestIdPrefix;
|
|
355
414
|
private requestCounter;
|
|
415
|
+
private requestGeneration;
|
|
416
|
+
private requestCancellationMessage;
|
|
417
|
+
private lifecycleTransition;
|
|
418
|
+
private disconnectInFlight;
|
|
419
|
+
private restrictedRequestActive;
|
|
420
|
+
private restrictedRequestQueue;
|
|
356
421
|
readonly isQRLConnect = true;
|
|
357
422
|
constructor(options: QRLConnectOptions);
|
|
358
423
|
private startEip6963Announce;
|
|
@@ -374,6 +439,10 @@ declare class QRLConnectProvider extends EventEmitter<ProviderEvents> {
|
|
|
374
439
|
*/
|
|
375
440
|
stopEip6963Announce(): void;
|
|
376
441
|
private setupConnectionListeners;
|
|
442
|
+
private handleJsonRpcResponse;
|
|
443
|
+
private cancelAllRequests;
|
|
444
|
+
private assertRequestGeneration;
|
|
445
|
+
private isCurrentChain;
|
|
377
446
|
/**
|
|
378
447
|
* Generate a connection URI for QR code display or deep link redirect.
|
|
379
448
|
*/
|
|
@@ -393,6 +462,15 @@ declare class QRLConnectProvider extends EventEmitter<ProviderEvents> {
|
|
|
393
462
|
method: string;
|
|
394
463
|
params?: unknown[];
|
|
395
464
|
}): Promise<unknown>;
|
|
465
|
+
/**
|
|
466
|
+
* Keep at most one approval-bound request active. Wallet approval screens
|
|
467
|
+
* are a serial human interaction, and request-id ordering must remain
|
|
468
|
+
* stable even when a dApp fires several signing calls concurrently.
|
|
469
|
+
*/
|
|
470
|
+
private enqueueRestrictedRequest;
|
|
471
|
+
private runRestrictedRequest;
|
|
472
|
+
private finishRestrictedRequest;
|
|
473
|
+
private performRequest;
|
|
396
474
|
/**
|
|
397
475
|
* Redirect only for calls the user must approve, on mobile, and only when
|
|
398
476
|
* the wallet is actually absent from the channel: right after pairing its
|
|
@@ -470,7 +548,7 @@ declare class QRLConnectProvider extends EventEmitter<ProviderEvents> {
|
|
|
470
548
|
/**
|
|
471
549
|
* Connection Manager - orchestrates socket lifecycle, the post-quantum
|
|
472
550
|
* handshake, encrypted message routing, and session persistence for the
|
|
473
|
-
* dApp side of QRL Connect
|
|
551
|
+
* dApp side of QRL Connect v3.
|
|
474
552
|
*/
|
|
475
553
|
|
|
476
554
|
interface ConnectionManagerEvents {
|
|
@@ -510,6 +588,13 @@ declare class ConnectionManager extends EventEmitter<ConnectionManagerEvents> {
|
|
|
510
588
|
private pendingRestore;
|
|
511
589
|
private messageQueue;
|
|
512
590
|
private outboundQueue;
|
|
591
|
+
private persistenceQueue;
|
|
592
|
+
private readonly storage;
|
|
593
|
+
private readonly persistenceEnabled;
|
|
594
|
+
private readonly sessionOwnership;
|
|
595
|
+
private persistenceBroken;
|
|
596
|
+
private sessionCreatedAt;
|
|
597
|
+
private sessionGeneration;
|
|
513
598
|
private consecutiveDecryptFailures;
|
|
514
599
|
private static MAX_RECONNECT_FAILURES;
|
|
515
600
|
private static MAX_DECRYPT_FAILURES;
|
|
@@ -520,9 +605,11 @@ declare class ConnectionManager extends EventEmitter<ConnectionManagerEvents> {
|
|
|
520
605
|
storageKey?: string | undefined;
|
|
521
606
|
});
|
|
522
607
|
private setupSocketListeners;
|
|
608
|
+
private handleAutoReconnected;
|
|
523
609
|
private setupKeyExchangeListeners;
|
|
524
610
|
/**
|
|
525
|
-
* Generate a new
|
|
611
|
+
* Generate a new v3 connection URI. Rotates channel id, keypair, and the
|
|
612
|
+
* QR-only pairing capability.
|
|
526
613
|
* Returns a `qrlconnect://?q=…` URI safe for QR-rendering or deep-link.
|
|
527
614
|
*/
|
|
528
615
|
getConnectionURI(retryOnConflict?: boolean): Promise<string>;
|
|
@@ -585,6 +672,11 @@ declare class ConnectionManager extends EventEmitter<ConnectionManagerEvents> {
|
|
|
585
672
|
sendJsonRpc(request: JsonRpcRequest): Promise<void>;
|
|
586
673
|
getStatus(): ConnectionStatus;
|
|
587
674
|
getAccounts(): string[];
|
|
675
|
+
/**
|
|
676
|
+
* Commit accounts returned by an approved qrl_requestAccounts response.
|
|
677
|
+
* WALLET_INFO alone cannot grant this authorization.
|
|
678
|
+
*/
|
|
679
|
+
authorizeAccounts(value: unknown): Promise<string[]>;
|
|
588
680
|
getChainId(): string;
|
|
589
681
|
getChannelId(): string;
|
|
590
682
|
/** Check (sync) if a persisted session exists and has not expired. */
|
|
@@ -597,6 +689,13 @@ declare class ConnectionManager extends EventEmitter<ConnectionManagerEvents> {
|
|
|
597
689
|
* instant disconnect. Mirrors the wallet side's pattern.
|
|
598
690
|
*/
|
|
599
691
|
private flushTerminate;
|
|
692
|
+
/**
|
|
693
|
+
* Ensure this socket is a relay participant before closing a restorable or
|
|
694
|
+
* temporarily disconnected channel. close_channel is intentionally
|
|
695
|
+
* participant-only on the relay, so a cold stored session cannot be retired
|
|
696
|
+
* by emitting the close event from an unjoined socket.
|
|
697
|
+
*/
|
|
698
|
+
private prepareRelayChannelForRetirement;
|
|
600
699
|
/**
|
|
601
700
|
* Reset to a fresh channel + keypair. Sends TERMINATE to any live peer,
|
|
602
701
|
* drops the persisted session, and prepares a clean state for
|
|
@@ -616,6 +715,7 @@ declare class ConnectionManager extends EventEmitter<ConnectionManagerEvents> {
|
|
|
616
715
|
* clear local state and surface the standard terminated teardown.
|
|
617
716
|
*/
|
|
618
717
|
private teardownDesyncedSession;
|
|
718
|
+
private teardownPersistenceFailedSession;
|
|
619
719
|
private handleSynAck;
|
|
620
720
|
private handleDecryptedMessage;
|
|
621
721
|
private sendPlaintext;
|
|
@@ -634,8 +734,17 @@ declare class ConnectionManager extends EventEmitter<ConnectionManagerEvents> {
|
|
|
634
734
|
private setStatus;
|
|
635
735
|
private isDappParticipantConflictError;
|
|
636
736
|
private areArraysEqual;
|
|
737
|
+
private invalidateSessionWork;
|
|
738
|
+
private captureSessionWorkContext;
|
|
739
|
+
private isSessionWorkCurrent;
|
|
740
|
+
private assertSessionWorkCurrent;
|
|
741
|
+
private acquireSessionOwnership;
|
|
742
|
+
private releaseSessionOwnership;
|
|
637
743
|
private persistSession;
|
|
744
|
+
private persistSessionNow;
|
|
638
745
|
private readStoredSession;
|
|
746
|
+
private removeStoredSession;
|
|
747
|
+
private removeStoredSessionOrThrow;
|
|
639
748
|
private clearSession;
|
|
640
749
|
}
|
|
641
750
|
|
|
@@ -644,7 +753,7 @@ declare class ConnectionManager extends EventEmitter<ConnectionManagerEvents> {
|
|
|
644
753
|
*/
|
|
645
754
|
|
|
646
755
|
interface SocketClientEvents {
|
|
647
|
-
message: (data:
|
|
756
|
+
message: (data: unknown) => void;
|
|
648
757
|
connected: () => void;
|
|
649
758
|
disconnected: (reason: string) => void;
|
|
650
759
|
reconnected: (result: JoinResult) => void;
|
|
@@ -658,7 +767,7 @@ interface JoinResult {
|
|
|
658
767
|
bufferedMessages: unknown[];
|
|
659
768
|
/**
|
|
660
769
|
* Base64-encoded KEM public key the relay has bound to this channel.
|
|
661
|
-
* Populated for wallet joins (
|
|
770
|
+
* Populated for wallet joins (v3 protocol). `null` if the dApp hasn't
|
|
662
771
|
* registered a PK yet - wallet callers must treat this as a retry signal.
|
|
663
772
|
*/
|
|
664
773
|
channelPublicKey: string | null;
|
|
@@ -683,6 +792,9 @@ declare class SocketClient extends EventEmitter<SocketClientEvents> {
|
|
|
683
792
|
private publicKeyBase64;
|
|
684
793
|
private seq;
|
|
685
794
|
private pendingJoin;
|
|
795
|
+
private pendingSends;
|
|
796
|
+
private pendingJoinAttempts;
|
|
797
|
+
private get expectedPeerType();
|
|
686
798
|
constructor(relayUrl: string, clientType: 'dapp' | 'wallet');
|
|
687
799
|
/**
|
|
688
800
|
* dApp-only: stash the PK so it gets uploaded on every join_channel
|
|
@@ -705,6 +817,7 @@ declare class SocketClient extends EventEmitter<SocketClientEvents> {
|
|
|
705
817
|
*/
|
|
706
818
|
joinChannel(channelId: string): Promise<JoinResult>;
|
|
707
819
|
private joinChannelNow;
|
|
820
|
+
private rejectPendingJoinAttempts;
|
|
708
821
|
/**
|
|
709
822
|
* Send a message through the relay.
|
|
710
823
|
*/
|
|
@@ -712,6 +825,7 @@ declare class SocketClient extends EventEmitter<SocketClientEvents> {
|
|
|
712
825
|
success: boolean;
|
|
713
826
|
buffered: boolean;
|
|
714
827
|
}>;
|
|
828
|
+
private rejectPendingSends;
|
|
715
829
|
/**
|
|
716
830
|
* Emit an event and resolve once the relay acks it, or after a bounded
|
|
717
831
|
* flush window. Lets a caller await transmission before tearing the
|
|
@@ -725,7 +839,7 @@ declare class SocketClient extends EventEmitter<SocketClientEvents> {
|
|
|
725
839
|
* peer could not open it). Resolves once the close is flushed or the
|
|
726
840
|
* bounded window elapses, so the caller can safely disconnect afterwards.
|
|
727
841
|
*/
|
|
728
|
-
closeChannel(): Promise<
|
|
842
|
+
closeChannel(): Promise<boolean>;
|
|
729
843
|
/**
|
|
730
844
|
* Leave the current channel.
|
|
731
845
|
*/
|
|
@@ -781,6 +895,18 @@ declare function computeMessageDigest(messageBytes: Uint8Array): Uint8Array;
|
|
|
781
895
|
* the wallet copy for the spec-level comments; behavior must stay in
|
|
782
896
|
* lock-step or the cross-repo parity test fails on next CI.
|
|
783
897
|
*/
|
|
898
|
+
declare const TYPED_DATA_LIMITS: Readonly<{
|
|
899
|
+
maxTypes: 32;
|
|
900
|
+
maxFieldsPerType: 32;
|
|
901
|
+
maxTotalFields: 256;
|
|
902
|
+
maxTypeGraphDepth: 12;
|
|
903
|
+
maxArrayNesting: 12;
|
|
904
|
+
maxArrayLength: 256;
|
|
905
|
+
maxEncodedValues: 2048;
|
|
906
|
+
maxDynamicBytes: number;
|
|
907
|
+
maxIdentifierLength: 64;
|
|
908
|
+
maxFieldTypeLength: 128;
|
|
909
|
+
}>;
|
|
784
910
|
type FieldType = string;
|
|
785
911
|
interface TypedField {
|
|
786
912
|
name: string;
|
|
@@ -800,28 +926,70 @@ declare function encodeType(primary: string, types: TypeMap): string;
|
|
|
800
926
|
declare function typeHash(primary: string, types: TypeMap): Uint8Array;
|
|
801
927
|
declare function encodeField(type: FieldType, value: unknown, types: TypeMap): Uint8Array;
|
|
802
928
|
declare function hashStruct(primary: string, data: Message, types: TypeMap): Uint8Array;
|
|
803
|
-
declare function computeTypedDataDigest(payload:
|
|
929
|
+
declare function computeTypedDataDigest(payload: unknown): Uint8Array;
|
|
804
930
|
|
|
805
931
|
/**
|
|
806
932
|
* Stateless verifiers for the two PQ signing schemes. The SDK exposes these
|
|
807
933
|
* so a dApp can re-verify the rich response object returned by the wallet
|
|
808
|
-
* without round-tripping back through the relay.
|
|
809
|
-
*
|
|
934
|
+
* without round-tripping back through the relay. The lower-level
|
|
935
|
+
* verifyMessageSignature / verifyTypedDataSignature prove only that a
|
|
936
|
+
* supplied public key made the signature. Authorization decisions should use
|
|
937
|
+
* the ForSigner variants, which also bind that key to the expected
|
|
938
|
+
* current-format address.
|
|
810
939
|
*/
|
|
811
940
|
|
|
941
|
+
declare const ML_DSA_DESCRIPTOR_BYTES = 3;
|
|
942
|
+
declare const ML_DSA_87_PUBLIC_KEY_BYTES = 2592;
|
|
943
|
+
declare const ML_DSA_87_SIGNATURE_BYTES = 4627;
|
|
812
944
|
interface VerifyMessageParams {
|
|
813
945
|
signature: Uint8Array | string;
|
|
814
946
|
publicKey: Uint8Array | string;
|
|
815
947
|
/** 0x-hex bytes that were originally signed. */
|
|
816
948
|
messageBytes: Uint8Array | string;
|
|
817
949
|
}
|
|
818
|
-
|
|
950
|
+
/**
|
|
951
|
+
* Verify against an explicitly supplied ML-DSA public key only. This does not
|
|
952
|
+
* prove that the key derives the signer claimed by a wallet response. Use
|
|
953
|
+
* verifyMessageForSigner for authentication or authorization decisions.
|
|
954
|
+
*/
|
|
955
|
+
declare function verifyMessageSignature({ signature, publicKey, messageBytes, }: VerifyMessageParams): boolean;
|
|
819
956
|
interface VerifyTypedDataParams {
|
|
820
957
|
signature: Uint8Array | string;
|
|
821
958
|
publicKey: Uint8Array | string;
|
|
822
959
|
payload: TypedDataPayload;
|
|
823
960
|
}
|
|
824
|
-
|
|
961
|
+
/**
|
|
962
|
+
* Verify against an explicitly supplied ML-DSA public key only. This does not
|
|
963
|
+
* bind that key to a Q-address. Use verifyTypedDataForSigner when signer
|
|
964
|
+
* identity matters.
|
|
965
|
+
*/
|
|
966
|
+
declare function verifyTypedDataSignature({ signature, publicKey, payload, }: VerifyTypedDataParams): boolean;
|
|
967
|
+
interface VerifyMessageForSignerParams extends VerifyMessageParams {
|
|
968
|
+
/** Current Q + 40 hex address expected by the dApp. */
|
|
969
|
+
expectedSigner: string;
|
|
970
|
+
/** Exact 3-byte wallet descriptor. Missing legacy values fail closed. */
|
|
971
|
+
descriptor?: Uint8Array | string | undefined;
|
|
972
|
+
}
|
|
973
|
+
/** Verify both the message signature and public-key-to-signer binding. */
|
|
974
|
+
declare function verifyMessageForSigner({ expectedSigner, descriptor, signature, publicKey, messageBytes, }: VerifyMessageForSignerParams): boolean;
|
|
975
|
+
interface VerifyTypedDataForSignerParams extends VerifyTypedDataParams {
|
|
976
|
+
/** Current Q + 40 hex address expected by the dApp. */
|
|
977
|
+
expectedSigner: string;
|
|
978
|
+
/** Exact 3-byte wallet descriptor. Missing legacy values fail closed. */
|
|
979
|
+
descriptor?: Uint8Array | string | undefined;
|
|
980
|
+
}
|
|
981
|
+
/** Verify both the typed-data signature and public-key-to-signer binding. */
|
|
982
|
+
declare function verifyTypedDataForSigner({ expectedSigner, descriptor, signature, publicKey, payload, }: VerifyTypedDataForSignerParams): boolean;
|
|
983
|
+
/**
|
|
984
|
+
* @deprecated Use verifyMessageSignature for a key-only check, or
|
|
985
|
+
* verifyMessageForSigner when signer identity matters.
|
|
986
|
+
*/
|
|
987
|
+
declare function verifyMessage(params: VerifyMessageParams): boolean;
|
|
988
|
+
/**
|
|
989
|
+
* @deprecated Use verifyTypedDataSignature for a key-only check, or
|
|
990
|
+
* verifyTypedDataForSigner when signer identity matters.
|
|
991
|
+
*/
|
|
992
|
+
declare function verifyTypedData(params: VerifyTypedDataParams): boolean;
|
|
825
993
|
|
|
826
994
|
/**
|
|
827
995
|
* Local hex/bytes utilities for the signing module.
|
|
@@ -833,38 +1001,40 @@ declare function bytesToHex(bytes: Uint8Array): string;
|
|
|
833
1001
|
declare function concatBytes(...parts: Uint8Array[]): Uint8Array;
|
|
834
1002
|
|
|
835
1003
|
/**
|
|
836
|
-
*
|
|
1004
|
+
* v3 QR URI codec (PQP3).
|
|
837
1005
|
*
|
|
838
|
-
* Format: qrlconnect://?q=<base45(
|
|
1006
|
+
* Format: qrlconnect://?q=<base45(PQP3 || cid || fp || cap)>[&r=<relayUrl>]
|
|
839
1007
|
*
|
|
840
|
-
*
|
|
841
|
-
* cid = 16 bytes (UUIDv4 raw)
|
|
842
|
-
* fp = SHA-256("pq-fp/
|
|
843
|
-
*
|
|
1008
|
+
* PQP3 = "PQP3" magic (4 B)
|
|
1009
|
+
* cid = 16 bytes (UUIDv4 raw) (16 B)
|
|
1010
|
+
* fp = SHA-256("pq-fp/v3" || cid || pk || cap) (full 32B) (32 B)
|
|
1011
|
+
* cap = fresh CSPRNG pairing capability (32 B)
|
|
1012
|
+
* total (84 B)
|
|
844
1013
|
*
|
|
845
|
-
*
|
|
846
|
-
*
|
|
1014
|
+
* The capability is a bearer secret. It is never uploaded to the relay and
|
|
1015
|
+
* must not be logged or persisted before the handshake consumes it.
|
|
847
1016
|
*
|
|
848
1017
|
* Security: the public key is NOT carried in the QR. It's uploaded by the
|
|
849
1018
|
* dApp to the relay at channel creation, the relay binds it to the cid, and
|
|
850
|
-
* the wallet fetches it via the join_channel ack. The
|
|
851
|
-
* the
|
|
852
|
-
*
|
|
853
|
-
*
|
|
854
|
-
* substitution of a maliciously-crafted PK with the same fingerprint.
|
|
1019
|
+
* the wallet fetches it via the join_channel ack. The fingerprint commits
|
|
1020
|
+
* the relay-served key to the exact out-of-band capability. The capability
|
|
1021
|
+
* is also bound into the transcript and HKDF by the key-exchange layer, so a
|
|
1022
|
+
* relay cannot fabricate an authenticated wallet hello.
|
|
855
1023
|
*
|
|
856
|
-
* Legacy
|
|
857
|
-
*
|
|
858
|
-
*
|
|
1024
|
+
* Legacy PQP1 and PQP2 URIs are rejected with a clear migration error. This
|
|
1025
|
+
* intentional v4.0 break prevents fallback to a pairing mode where the relay
|
|
1026
|
+
* could impersonate a wallet.
|
|
859
1027
|
*/
|
|
860
1028
|
declare const CID_LEN = 16;
|
|
861
1029
|
declare const FP_LEN = 32;
|
|
1030
|
+
declare const CAP_LEN = 32;
|
|
862
1031
|
declare const BLOB_LEN: number;
|
|
1032
|
+
declare const MAX_CONNECTION_URI_LENGTH = 4096;
|
|
863
1033
|
/**
|
|
864
|
-
* Compute the full 32-byte fingerprint binding (label || cid || pk).
|
|
1034
|
+
* Compute the full 32-byte fingerprint binding (label || cid || pk || cap).
|
|
865
1035
|
* Exported so the wallet side can re-derive and verify.
|
|
866
1036
|
*/
|
|
867
|
-
declare function computeFingerprint(cid: Uint8Array, pk: Uint8Array): Promise<Uint8Array>;
|
|
1037
|
+
declare function computeFingerprint(cid: Uint8Array, pk: Uint8Array, capability: Uint8Array): Promise<Uint8Array>;
|
|
868
1038
|
/**
|
|
869
1039
|
* Constant-time comparison of two byte arrays. Both must be the same length.
|
|
870
1040
|
* The verifier MUST use this rather than `===` or `indexOf` so timing signals
|
|
@@ -872,18 +1042,19 @@ declare function computeFingerprint(cid: Uint8Array, pk: Uint8Array): Promise<Ui
|
|
|
872
1042
|
*/
|
|
873
1043
|
declare function fingerprintEquals(a: Uint8Array, b: Uint8Array): boolean;
|
|
874
1044
|
/**
|
|
875
|
-
* Encode (cid, pk) as a qrlconnect:// URI.
|
|
876
|
-
*
|
|
877
|
-
* upload the PK to the relay separately before publishing this URI.
|
|
1045
|
+
* Encode (cid, pk, capability) as a qrlconnect:// URI. The PK is not stored
|
|
1046
|
+
* in the URI; only its capability-bound fingerprint is embedded. The caller
|
|
1047
|
+
* must upload the PK to the relay separately before publishing this URI.
|
|
878
1048
|
*
|
|
879
1049
|
* The optional `relayUrl` rides as a sibling query param (not inside the
|
|
880
1050
|
* fp-bound blob) so a tampered relay can cause the pairing to fail but
|
|
881
1051
|
* cannot substitute the PK - the fp still pins that.
|
|
882
1052
|
*/
|
|
883
|
-
declare function generateConnectionURI(cid: Uint8Array, pk: Uint8Array, relayUrl?: string): Promise<string>;
|
|
1053
|
+
declare function generateConnectionURI(cid: Uint8Array, pk: Uint8Array, capability: Uint8Array, relayUrl?: string): Promise<string>;
|
|
884
1054
|
interface ParsedURI {
|
|
885
1055
|
cid: Uint8Array;
|
|
886
1056
|
fp: Uint8Array;
|
|
1057
|
+
capability: Uint8Array;
|
|
887
1058
|
relayUrl?: string | undefined;
|
|
888
1059
|
}
|
|
889
1060
|
declare function parseConnectionURI(uri: string): Promise<ParsedURI>;
|
|
@@ -923,20 +1094,28 @@ declare function getAppStoreUrl(): string;
|
|
|
923
1094
|
declare function attemptWalletRedirect(uri: string, timeoutMs?: number): Promise<boolean>;
|
|
924
1095
|
|
|
925
1096
|
declare const DEFAULT_RELAY_URL = "https://qrlwallet.com";
|
|
926
|
-
declare const PROTOCOL_VERSION =
|
|
1097
|
+
declare const PROTOCOL_VERSION = 3;
|
|
1098
|
+
declare const PAIRING_CAPABILITY_LEN = 32;
|
|
1099
|
+
declare const MAX_JSON_RPC_ID_LENGTH = 128;
|
|
1100
|
+
declare const MAX_JSON_RPC_METHOD_LENGTH = 128;
|
|
1101
|
+
declare const MAX_RELAY_URL_LENGTH = 2048;
|
|
927
1102
|
/**
|
|
928
|
-
*
|
|
929
|
-
*
|
|
930
|
-
*
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
declare
|
|
939
|
-
|
|
940
|
-
|
|
1103
|
+
* Validate and canonicalize relay endpoints before they reach logging,
|
|
1104
|
+
* Socket.IO, or a QR URI. Production relays require HTTPS; plain HTTP is
|
|
1105
|
+
* limited to explicit loopback hostnames for local development.
|
|
1106
|
+
*/
|
|
1107
|
+
declare function normalizeRelayUrl(value: unknown): string;
|
|
1108
|
+
/** Public snapshots for discovery. Provider authorization uses private sets. */
|
|
1109
|
+
declare const RESTRICTED_METHODS: ReadonlySet<string>;
|
|
1110
|
+
declare const UNRESTRICTED_METHODS: ReadonlySet<string>;
|
|
1111
|
+
declare const EXPLICITLY_UNSUPPORTED_METHODS: ReadonlySet<string>;
|
|
1112
|
+
type RpcMethodPolicy = 'restricted' | 'unrestricted' | 'unsupported';
|
|
1113
|
+
declare function isValidJsonRpcId(value: unknown): value is string | number;
|
|
1114
|
+
declare function isValidJsonRpcMethod(value: unknown): value is string;
|
|
1115
|
+
/**
|
|
1116
|
+
* Classify an RPC method with a closed allowlist. Any method absent from the
|
|
1117
|
+
* two positive policy sets is unsupported, including future signing methods.
|
|
1118
|
+
*/
|
|
1119
|
+
declare function classifyRpcMethod(method: string): RpcMethodPolicy;
|
|
941
1120
|
|
|
942
|
-
export { type AckMessage, BLOB_LEN, CID_LEN, ConnectionManager, ConnectionStatus, type DAppMetadata, type DAppSession, DEFAULT_RELAY_URL, DIGEST_LEN, type Domain, type EIP6963ProviderInfoOverride, FP_LEN, type JsonRpcRequest, type JsonRpcResponse, KeyExchange, KeyExchangeMessageType, type Message, MessageType, PROTOCOL_VERSION, type ParsedURI, type PendingRequest, type PersistedSession, type ProviderEvents, QRLConnectProvider as QRLConnect, type QRLConnectOptions, QRLConnectProvider, QRL_CONNECT_PROVIDER_INFO, type QrlSignMessageParams, type QrlSignTypedDataParams, type QrlSignedResult, type QrlSignedTypedDataResult, type QrlTypedDataPayload, RESTRICTED_METHODS, type RelayMessage, SCHEME_TAG_MSG, SCHEME_TAG_TYPED, SCHEME_VERSION_MSG, SCHEME_VERSION_TYPED, type Session, SocketClient, type StructDef, type SynAckMessage, type TypeMap, type TypedDataPayload, type TypedField, UNRESTRICTED_METHODS, type VerifyMessageParams, type VerifyTypedDataParams, attemptWalletRedirect, bytesToHex, cidFromString, cidRandom, cidToString, computeFingerprint, computeMessageDigest, computeTypedDataDigest, concatBytes, encodeField, encodeType, fingerprintEquals, generateConnectionURI, getAppStoreUrl, hashStruct, hexToBytes, isMobileBrowser, parseConnectionURI, typeHash, verifyMessage, verifyTypedData };
|
|
1121
|
+
export { type AckMessage, BLOB_LEN, CAP_LEN, CID_LEN, ConnectionManager, ConnectionStatus, type DAppMetadata, type DAppSession, DEFAULT_RELAY_URL, DIGEST_LEN, type Domain, type EIP6963ProviderInfoOverride, EXPLICITLY_UNSUPPORTED_METHODS, FP_LEN, type InitiatedKeyExchange, type JsonRpcRequest, type JsonRpcResponse, KeyExchange, KeyExchangeMessageType, MAX_CONNECTION_URI_LENGTH, MAX_JSON_RPC_ID_LENGTH, MAX_JSON_RPC_METHOD_LENGTH, MAX_RELAY_URL_LENGTH, ML_DSA_87_PUBLIC_KEY_BYTES, ML_DSA_87_SIGNATURE_BYTES, ML_DSA_DESCRIPTOR_BYTES, type Message, MessageType, PAIRING_CAPABILITY_LEN, PROTOCOL_VERSION, type ParsedURI, type PendingRequest, type PersistedSession, type ProviderEvents, QRLConnectProvider as QRLConnect, type QRLConnectOptions, QRLConnectProvider, QRL_CONNECT_PROVIDER_INFO, type QrlSignMessageParams, type QrlSignTypedDataParams, type QrlSignedMessageResult, type QrlSignedResult, type QrlSignedResultWithDescriptor, type QrlSignedTypedDataResult, type QrlSigningResult, type QrlSigningSchemeVersion, type QrlTypedDataPayload, RESTRICTED_METHODS, type RelayMessage, type RpcMethodPolicy, SCHEME_TAG_MSG, SCHEME_TAG_TYPED, SCHEME_VERSION_MSG, SCHEME_VERSION_TYPED, type Session, SocketClient, type StructDef, type SynAckMessage, TYPED_DATA_LIMITS, type TypeMap, type TypedDataPayload, type TypedField, UNRESTRICTED_METHODS, type VerifyMessageForSignerParams, type VerifyMessageParams, type VerifyTypedDataForSignerParams, type VerifyTypedDataParams, attemptWalletRedirect, bytesToHex, cidFromString, cidRandom, cidToString, classifyRpcMethod, computeFingerprint, computeMessageDigest, computeTypedDataDigest, concatBytes, encodeField, encodeType, fingerprintEquals, generateConnectionURI, getAppStoreUrl, hasSigningDescriptor, hashStruct, hexToBytes, isMobileBrowser, isQrlSignedMessageResult, isQrlSignedResult, isQrlSignedTypedDataResult, isValidJsonRpcId, isValidJsonRpcMethod, normalizeRelayUrl, parseConnectionURI, typeHash, verifyMessage, verifyMessageForSigner, verifyMessageSignature, verifyTypedData, verifyTypedDataForSigner, verifyTypedDataSignature };
|