@qrlwallet/connect 5.0.3 → 5.1.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 +7 -6
- package/dist/index.d.mts +61 -20
- package/dist/index.d.ts +61 -20
- package/dist/index.js +100 -49
- package/dist/index.mjs +96 -49
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,8 +9,9 @@ Account validation and message-signature verification use the complete 64-byte
|
|
|
9
9
|
address. Applications using legacy 20-byte addresses must remain on version 4
|
|
10
10
|
until their network and wallet support QIP-55.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
`qrl_signTypedData` signs payloads with QIP-55 address fields under
|
|
13
|
+
`QRL-SIGN-TYPED-v2` (one 64-byte word per value); address-free payloads keep
|
|
14
|
+
`QRL-SIGN-TYPED-v1`. See docs/JSON-RPC-REFERENCE.md.
|
|
14
15
|
The private v3 network is a test network; its availability does not establish
|
|
15
16
|
compatibility with legacy deployments or contracts.
|
|
16
17
|
|
|
@@ -173,16 +174,16 @@ The main class. Creates a connection manager and EIP-1193 provider.
|
|
|
173
174
|
**Require user approval:**
|
|
174
175
|
`qrl_requestAccounts`, `qrl_sendTransaction`, `qrl_signTransaction`, `qrl_signMessage`, `qrl_signTypedData`, `wallet_switchQrlChain`
|
|
175
176
|
|
|
176
|
-
`qrl_signMessage`
|
|
177
|
+
`qrl_signMessage` and `qrl_signTypedData` use SHAKE256 plus native ML-DSA-87 ctx and return a rich `{ signature, publicKey, descriptor, signer, digest, schemeVersion }` object (typed data also echoes `domain`). Typed data with QIP-55 address fields is signed under `QRL-SIGN-TYPED-v2`, address-free typed data under `QRL-SIGN-TYPED-v1`.
|
|
177
178
|
|
|
178
179
|
`request()` returns `unknown`. First validate the exact response shape with
|
|
179
180
|
`isQrlSignedMessageResult` or `isQrlSignedTypedDataResult`, then require
|
|
180
181
|
`hasSigningDescriptor`. PQP3 authenticates the paired transport session. It
|
|
181
182
|
does not prove that the returned ML-DSA signature is valid or that its public
|
|
182
183
|
key belongs to the claimed signer. Authentication and authorization flows must
|
|
183
|
-
call `verifyMessageForSigner` or
|
|
184
|
-
with the original challenge or payload and the
|
|
185
|
-
address.
|
|
184
|
+
call `verifyMessageForSigner` or `verifyTypedDataForSigner` (pass the
|
|
185
|
+
response's `schemeVersion`) with the original challenge or payload and the
|
|
186
|
+
expected QIP-55 Q + 128 hex address.
|
|
186
187
|
|
|
187
188
|
The lower-level `verifyMessageSignature` / `verifyTypedDataSignature` helpers
|
|
188
189
|
verify only the supplied key and signature; they do not prove that the key
|
package/dist/index.d.mts
CHANGED
|
@@ -269,8 +269,8 @@ interface EIP6963ProviderInfoOverride {
|
|
|
269
269
|
rdns?: string;
|
|
270
270
|
}
|
|
271
271
|
/**
|
|
272
|
-
* Rich response object returned by `qrl_signMessage` and
|
|
273
|
-
* `qrl_signTypedData
|
|
272
|
+
* Rich response object returned by `qrl_signMessage` and
|
|
273
|
+
* `qrl_signTypedData`. The signature alone is not enough to
|
|
274
274
|
* verify since ML-DSA-87 public keys cannot be recovered from a signature,
|
|
275
275
|
* so the wallet returns the public key explicitly. Stateless verifiers need
|
|
276
276
|
* the original payload plus the signature fields below.
|
|
@@ -290,24 +290,26 @@ interface QrlSignedResult {
|
|
|
290
290
|
signer: string;
|
|
291
291
|
/** 0x-hex of the 64-byte SHAKE256 digest that was signed. */
|
|
292
292
|
digest: string;
|
|
293
|
-
/** Scheme tag: 'QRL-SIGN-MSG-v1' or 'QRL-SIGN-TYPED-
|
|
293
|
+
/** Scheme tag: 'QRL-SIGN-MSG-v1', 'QRL-SIGN-TYPED-v1' or 'QRL-SIGN-TYPED-v2'. */
|
|
294
294
|
schemeVersion: QrlSigningSchemeVersion;
|
|
295
295
|
}
|
|
296
296
|
/** A current wallet response whose descriptor can be signer-bound. */
|
|
297
297
|
interface QrlSignedResultWithDescriptor extends QrlSignedResult {
|
|
298
298
|
descriptor: string;
|
|
299
299
|
}
|
|
300
|
-
type QrlSigningSchemeVersion = 'QRL-SIGN-MSG-v1' | 'QRL-SIGN-TYPED-v1';
|
|
300
|
+
type QrlSigningSchemeVersion = 'QRL-SIGN-MSG-v1' | 'QRL-SIGN-TYPED-v1' | 'QRL-SIGN-TYPED-v2';
|
|
301
301
|
/** Strict wire shape returned by `qrl_signMessage`. */
|
|
302
302
|
interface QrlSignedMessageResult extends QrlSignedResult {
|
|
303
303
|
schemeVersion: 'QRL-SIGN-MSG-v1';
|
|
304
304
|
}
|
|
305
305
|
/**
|
|
306
|
-
*
|
|
307
|
-
*
|
|
306
|
+
* Response from `qrl_signTypedData`. It echoes `domain` so a stateless
|
|
307
|
+
* verifier does not need to be told the domain out-of-band. The scheme is
|
|
308
|
+
* v2 when the payload's types reach an `address` field and v1 otherwise;
|
|
309
|
+
* pass `schemeVersion` to `verifyTypedDataForSigner` to have it checked.
|
|
308
310
|
*/
|
|
309
311
|
interface QrlSignedTypedDataResult extends QrlSignedResult {
|
|
310
|
-
schemeVersion: 'QRL-SIGN-TYPED-v1';
|
|
312
|
+
schemeVersion: 'QRL-SIGN-TYPED-v1' | 'QRL-SIGN-TYPED-v2';
|
|
311
313
|
domain: Record<string, unknown>;
|
|
312
314
|
}
|
|
313
315
|
/** Every strict signing-result wire shape understood by this SDK. */
|
|
@@ -338,10 +340,9 @@ declare function hasSigningDescriptor(result: unknown): result is QrlSigningResu
|
|
|
338
340
|
*/
|
|
339
341
|
type QrlSignMessageParams = [string, string];
|
|
340
342
|
/**
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
* defined.
|
|
343
|
+
* `qrl_signTypedData` params: `[signer, payload]`. A payload whose types
|
|
344
|
+
* reach an `address` field is signed under QRL-SIGN-TYPED-v2, any other
|
|
345
|
+
* under v1.
|
|
345
346
|
*/
|
|
346
347
|
interface QrlTypedDataPayload {
|
|
347
348
|
types: Record<string, readonly {
|
|
@@ -870,8 +871,17 @@ declare class SocketClient extends EventEmitter<SocketClientEvents> {
|
|
|
870
871
|
*/
|
|
871
872
|
declare const SCHEME_VERSION_MSG = "QRL-SIGN-MSG-v1";
|
|
872
873
|
declare const SCHEME_VERSION_TYPED = "QRL-SIGN-TYPED-v1";
|
|
874
|
+
/**
|
|
875
|
+
* Typed data with 64-byte slots, one QRVM word per atomic value, so a QIP-55
|
|
876
|
+
* address fits. A payload uses it exactly when an `address` type is
|
|
877
|
+
* reachable from QRLDomain or the primary type; every other payload keeps
|
|
878
|
+
* v1. See typedData.ts.
|
|
879
|
+
*/
|
|
880
|
+
declare const SCHEME_VERSION_TYPED_V2 = "QRL-SIGN-TYPED-v2";
|
|
881
|
+
type TypedDataSchemeVersion = typeof SCHEME_VERSION_TYPED | typeof SCHEME_VERSION_TYPED_V2;
|
|
873
882
|
declare const SCHEME_TAG_MSG: Uint8Array;
|
|
874
883
|
declare const SCHEME_TAG_TYPED: Uint8Array;
|
|
884
|
+
declare const SCHEME_TAG_TYPED_V2: Uint8Array;
|
|
875
885
|
/** SHAKE256 output length used throughout the signing module (NIST L5-matched). */
|
|
876
886
|
declare const DIGEST_LEN = 64;
|
|
877
887
|
|
|
@@ -887,7 +897,7 @@ declare const DIGEST_LEN = 64;
|
|
|
887
897
|
declare function computeMessageDigest(messageBytes: Uint8Array): Uint8Array;
|
|
888
898
|
|
|
889
899
|
/**
|
|
890
|
-
*
|
|
900
|
+
* EIP-712-shaped typed-data encoding for `qrl_signTypedData`.
|
|
891
901
|
*
|
|
892
902
|
* Byte-identical algorithm with the wallet's signing/typedData.ts; this
|
|
893
903
|
* file is the SDK port (only the import block differs: the SDK routes
|
|
@@ -895,10 +905,19 @@ declare function computeMessageDigest(messageBytes: Uint8Array): Uint8Array;
|
|
|
895
905
|
* the wallet copy for the spec-level comments; behavior must stay in
|
|
896
906
|
* lock-step or the cross-repo parity test fails on next CI.
|
|
897
907
|
*
|
|
898
|
-
*
|
|
899
|
-
*
|
|
900
|
-
*
|
|
908
|
+
* Two schemes share this codec and differ only in the atomic slot size:
|
|
909
|
+
* - QRL-SIGN-TYPED-v1: 32-byte slots, the original layout.
|
|
910
|
+
* - QRL-SIGN-TYPED-v2: 64-byte slots, one QRVM word per atomic value, so a
|
|
911
|
+
* 64-byte QIP-55 address fits one slot. Integers are zero- or
|
|
912
|
+
* sign-extended over 512 bits, bool and address are left-padded, bytesN
|
|
913
|
+
* is right-padded.
|
|
914
|
+
* Strings, bytes, arrays, structs and type hashes are 64-byte SHAKE256
|
|
915
|
+
* digests in both. A payload uses v2 exactly when an `address` type
|
|
916
|
+
* (including address arrays) is reachable from QRLDomain or the primary
|
|
917
|
+
* type, so address-free payloads keep their v1 digests and older verifiers
|
|
918
|
+
* keep working for them.
|
|
901
919
|
*/
|
|
920
|
+
|
|
902
921
|
declare const TYPED_DATA_LIMITS: Readonly<{
|
|
903
922
|
maxTypes: 32;
|
|
904
923
|
maxFieldsPerType: 32;
|
|
@@ -928,8 +947,25 @@ interface TypedDataPayload {
|
|
|
928
947
|
}
|
|
929
948
|
declare function encodeType(primary: string, types: TypeMap): string;
|
|
930
949
|
declare function typeHash(primary: string, types: TypeMap): Uint8Array;
|
|
931
|
-
|
|
932
|
-
|
|
950
|
+
/**
|
|
951
|
+
* Encode one field. The scheme defaults to the one `types` selects (v2 when
|
|
952
|
+
* any struct has an address field), which matches computeTypedDataDigest
|
|
953
|
+
* when `types` is a payload's full type map.
|
|
954
|
+
*/
|
|
955
|
+
declare function encodeField(type: FieldType, value: unknown, types: TypeMap, schemeVersion?: TypedDataSchemeVersion): Uint8Array;
|
|
956
|
+
/**
|
|
957
|
+
* Hash one struct. The scheme defaults to the one `types` selects (v2 when
|
|
958
|
+
* any struct has an address field), which matches computeTypedDataDigest
|
|
959
|
+
* when `types` is a payload's full type map.
|
|
960
|
+
*/
|
|
961
|
+
declare function hashStruct(primary: string, data: Message, types: TypeMap, schemeVersion?: TypedDataSchemeVersion): Uint8Array;
|
|
962
|
+
/**
|
|
963
|
+
* The scheme a payload is signed and verified under. It follows from the
|
|
964
|
+
* payload's types alone, so wallet and verifier agree without negotiating.
|
|
965
|
+
*/
|
|
966
|
+
declare function typedDataSchemeVersion(payload: unknown): TypedDataSchemeVersion;
|
|
967
|
+
/** The digest-preimage and ML-DSA-87 ctx tag of a typed-data scheme. */
|
|
968
|
+
declare function typedDataSchemeTag(schemeVersion: TypedDataSchemeVersion): Uint8Array;
|
|
933
969
|
declare function computeTypedDataDigest(payload: unknown): Uint8Array;
|
|
934
970
|
|
|
935
971
|
/**
|
|
@@ -961,13 +997,18 @@ interface VerifyTypedDataParams {
|
|
|
961
997
|
signature: Uint8Array | string;
|
|
962
998
|
publicKey: Uint8Array | string;
|
|
963
999
|
payload: TypedDataPayload;
|
|
1000
|
+
/**
|
|
1001
|
+
* The `schemeVersion` from the wallet response. Optional: the payload's
|
|
1002
|
+
* types decide the scheme, and a supplied value must match it.
|
|
1003
|
+
*/
|
|
1004
|
+
schemeVersion?: string | undefined;
|
|
964
1005
|
}
|
|
965
1006
|
/**
|
|
966
1007
|
* Verify against an explicitly supplied ML-DSA public key only. This does not
|
|
967
1008
|
* bind that key to a Q-address. Use verifyTypedDataForSigner when signer
|
|
968
1009
|
* identity matters.
|
|
969
1010
|
*/
|
|
970
|
-
declare function verifyTypedDataSignature({ signature, publicKey, payload, }: VerifyTypedDataParams): boolean;
|
|
1011
|
+
declare function verifyTypedDataSignature({ signature, publicKey, payload, schemeVersion, }: VerifyTypedDataParams): boolean;
|
|
971
1012
|
interface VerifyMessageForSignerParams extends VerifyMessageParams {
|
|
972
1013
|
/** QIP-55 Q + 128 hex address expected by the dApp. */
|
|
973
1014
|
expectedSigner: string;
|
|
@@ -983,7 +1024,7 @@ interface VerifyTypedDataForSignerParams extends VerifyTypedDataParams {
|
|
|
983
1024
|
descriptor?: Uint8Array | string | undefined;
|
|
984
1025
|
}
|
|
985
1026
|
/** Verify both the typed-data signature and public-key-to-signer binding. */
|
|
986
|
-
declare function verifyTypedDataForSigner({ expectedSigner, descriptor, signature, publicKey, payload, }: VerifyTypedDataForSignerParams): boolean;
|
|
1027
|
+
declare function verifyTypedDataForSigner({ expectedSigner, descriptor, signature, publicKey, payload, schemeVersion, }: VerifyTypedDataForSignerParams): boolean;
|
|
987
1028
|
/**
|
|
988
1029
|
* @deprecated Use verifyMessageSignature for a key-only check, or
|
|
989
1030
|
* verifyMessageForSigner when signer identity matters.
|
|
@@ -1135,4 +1176,4 @@ declare function isValidJsonRpcMethod(value: unknown): value is string;
|
|
|
1135
1176
|
*/
|
|
1136
1177
|
declare function classifyRpcMethod(method: string): RpcMethodPolicy;
|
|
1137
1178
|
|
|
1138
|
-
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_ADDRESS_BYTES, QRL_ADDRESS_HEX_LENGTH, 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, formatQrlAddressFingerprint, generateConnectionURI, getAppStoreUrl, hasSigningDescriptor, hashStruct, hexToBytes, isCurrentQrlAddress, isMobileBrowser, isQrlSignedMessageResult, isQrlSignedResult, isQrlSignedTypedDataResult, isValidJsonRpcId, isValidJsonRpcMethod, normalizeRelayUrl, parseConnectionURI, qip55AddressFromBytes, typeHash, verifyMessage, verifyMessageForSigner, verifyMessageSignature, verifyTypedData, verifyTypedDataForSigner, verifyTypedDataSignature };
|
|
1179
|
+
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_ADDRESS_BYTES, QRL_ADDRESS_HEX_LENGTH, 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_TAG_TYPED_V2, SCHEME_VERSION_MSG, SCHEME_VERSION_TYPED, SCHEME_VERSION_TYPED_V2, type Session, SocketClient, type StructDef, type SynAckMessage, TYPED_DATA_LIMITS, type TypeMap, type TypedDataPayload, type TypedDataSchemeVersion, 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, formatQrlAddressFingerprint, generateConnectionURI, getAppStoreUrl, hasSigningDescriptor, hashStruct, hexToBytes, isCurrentQrlAddress, isMobileBrowser, isQrlSignedMessageResult, isQrlSignedResult, isQrlSignedTypedDataResult, isValidJsonRpcId, isValidJsonRpcMethod, normalizeRelayUrl, parseConnectionURI, qip55AddressFromBytes, typeHash, typedDataSchemeTag, typedDataSchemeVersion, verifyMessage, verifyMessageForSigner, verifyMessageSignature, verifyTypedData, verifyTypedDataForSigner, verifyTypedDataSignature };
|
package/dist/index.d.ts
CHANGED
|
@@ -269,8 +269,8 @@ interface EIP6963ProviderInfoOverride {
|
|
|
269
269
|
rdns?: string;
|
|
270
270
|
}
|
|
271
271
|
/**
|
|
272
|
-
* Rich response object returned by `qrl_signMessage` and
|
|
273
|
-
* `qrl_signTypedData
|
|
272
|
+
* Rich response object returned by `qrl_signMessage` and
|
|
273
|
+
* `qrl_signTypedData`. The signature alone is not enough to
|
|
274
274
|
* verify since ML-DSA-87 public keys cannot be recovered from a signature,
|
|
275
275
|
* so the wallet returns the public key explicitly. Stateless verifiers need
|
|
276
276
|
* the original payload plus the signature fields below.
|
|
@@ -290,24 +290,26 @@ interface QrlSignedResult {
|
|
|
290
290
|
signer: string;
|
|
291
291
|
/** 0x-hex of the 64-byte SHAKE256 digest that was signed. */
|
|
292
292
|
digest: string;
|
|
293
|
-
/** Scheme tag: 'QRL-SIGN-MSG-v1' or 'QRL-SIGN-TYPED-
|
|
293
|
+
/** Scheme tag: 'QRL-SIGN-MSG-v1', 'QRL-SIGN-TYPED-v1' or 'QRL-SIGN-TYPED-v2'. */
|
|
294
294
|
schemeVersion: QrlSigningSchemeVersion;
|
|
295
295
|
}
|
|
296
296
|
/** A current wallet response whose descriptor can be signer-bound. */
|
|
297
297
|
interface QrlSignedResultWithDescriptor extends QrlSignedResult {
|
|
298
298
|
descriptor: string;
|
|
299
299
|
}
|
|
300
|
-
type QrlSigningSchemeVersion = 'QRL-SIGN-MSG-v1' | 'QRL-SIGN-TYPED-v1';
|
|
300
|
+
type QrlSigningSchemeVersion = 'QRL-SIGN-MSG-v1' | 'QRL-SIGN-TYPED-v1' | 'QRL-SIGN-TYPED-v2';
|
|
301
301
|
/** Strict wire shape returned by `qrl_signMessage`. */
|
|
302
302
|
interface QrlSignedMessageResult extends QrlSignedResult {
|
|
303
303
|
schemeVersion: 'QRL-SIGN-MSG-v1';
|
|
304
304
|
}
|
|
305
305
|
/**
|
|
306
|
-
*
|
|
307
|
-
*
|
|
306
|
+
* Response from `qrl_signTypedData`. It echoes `domain` so a stateless
|
|
307
|
+
* verifier does not need to be told the domain out-of-band. The scheme is
|
|
308
|
+
* v2 when the payload's types reach an `address` field and v1 otherwise;
|
|
309
|
+
* pass `schemeVersion` to `verifyTypedDataForSigner` to have it checked.
|
|
308
310
|
*/
|
|
309
311
|
interface QrlSignedTypedDataResult extends QrlSignedResult {
|
|
310
|
-
schemeVersion: 'QRL-SIGN-TYPED-v1';
|
|
312
|
+
schemeVersion: 'QRL-SIGN-TYPED-v1' | 'QRL-SIGN-TYPED-v2';
|
|
311
313
|
domain: Record<string, unknown>;
|
|
312
314
|
}
|
|
313
315
|
/** Every strict signing-result wire shape understood by this SDK. */
|
|
@@ -338,10 +340,9 @@ declare function hasSigningDescriptor(result: unknown): result is QrlSigningResu
|
|
|
338
340
|
*/
|
|
339
341
|
type QrlSignMessageParams = [string, string];
|
|
340
342
|
/**
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
* defined.
|
|
343
|
+
* `qrl_signTypedData` params: `[signer, payload]`. A payload whose types
|
|
344
|
+
* reach an `address` field is signed under QRL-SIGN-TYPED-v2, any other
|
|
345
|
+
* under v1.
|
|
345
346
|
*/
|
|
346
347
|
interface QrlTypedDataPayload {
|
|
347
348
|
types: Record<string, readonly {
|
|
@@ -870,8 +871,17 @@ declare class SocketClient extends EventEmitter<SocketClientEvents> {
|
|
|
870
871
|
*/
|
|
871
872
|
declare const SCHEME_VERSION_MSG = "QRL-SIGN-MSG-v1";
|
|
872
873
|
declare const SCHEME_VERSION_TYPED = "QRL-SIGN-TYPED-v1";
|
|
874
|
+
/**
|
|
875
|
+
* Typed data with 64-byte slots, one QRVM word per atomic value, so a QIP-55
|
|
876
|
+
* address fits. A payload uses it exactly when an `address` type is
|
|
877
|
+
* reachable from QRLDomain or the primary type; every other payload keeps
|
|
878
|
+
* v1. See typedData.ts.
|
|
879
|
+
*/
|
|
880
|
+
declare const SCHEME_VERSION_TYPED_V2 = "QRL-SIGN-TYPED-v2";
|
|
881
|
+
type TypedDataSchemeVersion = typeof SCHEME_VERSION_TYPED | typeof SCHEME_VERSION_TYPED_V2;
|
|
873
882
|
declare const SCHEME_TAG_MSG: Uint8Array;
|
|
874
883
|
declare const SCHEME_TAG_TYPED: Uint8Array;
|
|
884
|
+
declare const SCHEME_TAG_TYPED_V2: Uint8Array;
|
|
875
885
|
/** SHAKE256 output length used throughout the signing module (NIST L5-matched). */
|
|
876
886
|
declare const DIGEST_LEN = 64;
|
|
877
887
|
|
|
@@ -887,7 +897,7 @@ declare const DIGEST_LEN = 64;
|
|
|
887
897
|
declare function computeMessageDigest(messageBytes: Uint8Array): Uint8Array;
|
|
888
898
|
|
|
889
899
|
/**
|
|
890
|
-
*
|
|
900
|
+
* EIP-712-shaped typed-data encoding for `qrl_signTypedData`.
|
|
891
901
|
*
|
|
892
902
|
* Byte-identical algorithm with the wallet's signing/typedData.ts; this
|
|
893
903
|
* file is the SDK port (only the import block differs: the SDK routes
|
|
@@ -895,10 +905,19 @@ declare function computeMessageDigest(messageBytes: Uint8Array): Uint8Array;
|
|
|
895
905
|
* the wallet copy for the spec-level comments; behavior must stay in
|
|
896
906
|
* lock-step or the cross-repo parity test fails on next CI.
|
|
897
907
|
*
|
|
898
|
-
*
|
|
899
|
-
*
|
|
900
|
-
*
|
|
908
|
+
* Two schemes share this codec and differ only in the atomic slot size:
|
|
909
|
+
* - QRL-SIGN-TYPED-v1: 32-byte slots, the original layout.
|
|
910
|
+
* - QRL-SIGN-TYPED-v2: 64-byte slots, one QRVM word per atomic value, so a
|
|
911
|
+
* 64-byte QIP-55 address fits one slot. Integers are zero- or
|
|
912
|
+
* sign-extended over 512 bits, bool and address are left-padded, bytesN
|
|
913
|
+
* is right-padded.
|
|
914
|
+
* Strings, bytes, arrays, structs and type hashes are 64-byte SHAKE256
|
|
915
|
+
* digests in both. A payload uses v2 exactly when an `address` type
|
|
916
|
+
* (including address arrays) is reachable from QRLDomain or the primary
|
|
917
|
+
* type, so address-free payloads keep their v1 digests and older verifiers
|
|
918
|
+
* keep working for them.
|
|
901
919
|
*/
|
|
920
|
+
|
|
902
921
|
declare const TYPED_DATA_LIMITS: Readonly<{
|
|
903
922
|
maxTypes: 32;
|
|
904
923
|
maxFieldsPerType: 32;
|
|
@@ -928,8 +947,25 @@ interface TypedDataPayload {
|
|
|
928
947
|
}
|
|
929
948
|
declare function encodeType(primary: string, types: TypeMap): string;
|
|
930
949
|
declare function typeHash(primary: string, types: TypeMap): Uint8Array;
|
|
931
|
-
|
|
932
|
-
|
|
950
|
+
/**
|
|
951
|
+
* Encode one field. The scheme defaults to the one `types` selects (v2 when
|
|
952
|
+
* any struct has an address field), which matches computeTypedDataDigest
|
|
953
|
+
* when `types` is a payload's full type map.
|
|
954
|
+
*/
|
|
955
|
+
declare function encodeField(type: FieldType, value: unknown, types: TypeMap, schemeVersion?: TypedDataSchemeVersion): Uint8Array;
|
|
956
|
+
/**
|
|
957
|
+
* Hash one struct. The scheme defaults to the one `types` selects (v2 when
|
|
958
|
+
* any struct has an address field), which matches computeTypedDataDigest
|
|
959
|
+
* when `types` is a payload's full type map.
|
|
960
|
+
*/
|
|
961
|
+
declare function hashStruct(primary: string, data: Message, types: TypeMap, schemeVersion?: TypedDataSchemeVersion): Uint8Array;
|
|
962
|
+
/**
|
|
963
|
+
* The scheme a payload is signed and verified under. It follows from the
|
|
964
|
+
* payload's types alone, so wallet and verifier agree without negotiating.
|
|
965
|
+
*/
|
|
966
|
+
declare function typedDataSchemeVersion(payload: unknown): TypedDataSchemeVersion;
|
|
967
|
+
/** The digest-preimage and ML-DSA-87 ctx tag of a typed-data scheme. */
|
|
968
|
+
declare function typedDataSchemeTag(schemeVersion: TypedDataSchemeVersion): Uint8Array;
|
|
933
969
|
declare function computeTypedDataDigest(payload: unknown): Uint8Array;
|
|
934
970
|
|
|
935
971
|
/**
|
|
@@ -961,13 +997,18 @@ interface VerifyTypedDataParams {
|
|
|
961
997
|
signature: Uint8Array | string;
|
|
962
998
|
publicKey: Uint8Array | string;
|
|
963
999
|
payload: TypedDataPayload;
|
|
1000
|
+
/**
|
|
1001
|
+
* The `schemeVersion` from the wallet response. Optional: the payload's
|
|
1002
|
+
* types decide the scheme, and a supplied value must match it.
|
|
1003
|
+
*/
|
|
1004
|
+
schemeVersion?: string | undefined;
|
|
964
1005
|
}
|
|
965
1006
|
/**
|
|
966
1007
|
* Verify against an explicitly supplied ML-DSA public key only. This does not
|
|
967
1008
|
* bind that key to a Q-address. Use verifyTypedDataForSigner when signer
|
|
968
1009
|
* identity matters.
|
|
969
1010
|
*/
|
|
970
|
-
declare function verifyTypedDataSignature({ signature, publicKey, payload, }: VerifyTypedDataParams): boolean;
|
|
1011
|
+
declare function verifyTypedDataSignature({ signature, publicKey, payload, schemeVersion, }: VerifyTypedDataParams): boolean;
|
|
971
1012
|
interface VerifyMessageForSignerParams extends VerifyMessageParams {
|
|
972
1013
|
/** QIP-55 Q + 128 hex address expected by the dApp. */
|
|
973
1014
|
expectedSigner: string;
|
|
@@ -983,7 +1024,7 @@ interface VerifyTypedDataForSignerParams extends VerifyTypedDataParams {
|
|
|
983
1024
|
descriptor?: Uint8Array | string | undefined;
|
|
984
1025
|
}
|
|
985
1026
|
/** Verify both the typed-data signature and public-key-to-signer binding. */
|
|
986
|
-
declare function verifyTypedDataForSigner({ expectedSigner, descriptor, signature, publicKey, payload, }: VerifyTypedDataForSignerParams): boolean;
|
|
1027
|
+
declare function verifyTypedDataForSigner({ expectedSigner, descriptor, signature, publicKey, payload, schemeVersion, }: VerifyTypedDataForSignerParams): boolean;
|
|
987
1028
|
/**
|
|
988
1029
|
* @deprecated Use verifyMessageSignature for a key-only check, or
|
|
989
1030
|
* verifyMessageForSigner when signer identity matters.
|
|
@@ -1135,4 +1176,4 @@ declare function isValidJsonRpcMethod(value: unknown): value is string;
|
|
|
1135
1176
|
*/
|
|
1136
1177
|
declare function classifyRpcMethod(method: string): RpcMethodPolicy;
|
|
1137
1178
|
|
|
1138
|
-
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_ADDRESS_BYTES, QRL_ADDRESS_HEX_LENGTH, 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, formatQrlAddressFingerprint, generateConnectionURI, getAppStoreUrl, hasSigningDescriptor, hashStruct, hexToBytes, isCurrentQrlAddress, isMobileBrowser, isQrlSignedMessageResult, isQrlSignedResult, isQrlSignedTypedDataResult, isValidJsonRpcId, isValidJsonRpcMethod, normalizeRelayUrl, parseConnectionURI, qip55AddressFromBytes, typeHash, verifyMessage, verifyMessageForSigner, verifyMessageSignature, verifyTypedData, verifyTypedDataForSigner, verifyTypedDataSignature };
|
|
1179
|
+
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_ADDRESS_BYTES, QRL_ADDRESS_HEX_LENGTH, 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_TAG_TYPED_V2, SCHEME_VERSION_MSG, SCHEME_VERSION_TYPED, SCHEME_VERSION_TYPED_V2, type Session, SocketClient, type StructDef, type SynAckMessage, TYPED_DATA_LIMITS, type TypeMap, type TypedDataPayload, type TypedDataSchemeVersion, 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, formatQrlAddressFingerprint, generateConnectionURI, getAppStoreUrl, hasSigningDescriptor, hashStruct, hexToBytes, isCurrentQrlAddress, isMobileBrowser, isQrlSignedMessageResult, isQrlSignedResult, isQrlSignedTypedDataResult, isValidJsonRpcId, isValidJsonRpcMethod, normalizeRelayUrl, parseConnectionURI, qip55AddressFromBytes, typeHash, typedDataSchemeTag, typedDataSchemeVersion, verifyMessage, verifyMessageForSigner, verifyMessageSignature, verifyTypedData, verifyTypedDataForSigner, verifyTypedDataSignature };
|
package/dist/index.js
CHANGED
|
@@ -59,8 +59,10 @@ __export(index_exports, {
|
|
|
59
59
|
RESTRICTED_METHODS: () => RESTRICTED_METHODS,
|
|
60
60
|
SCHEME_TAG_MSG: () => SCHEME_TAG_MSG,
|
|
61
61
|
SCHEME_TAG_TYPED: () => SCHEME_TAG_TYPED,
|
|
62
|
+
SCHEME_TAG_TYPED_V2: () => SCHEME_TAG_TYPED_V2,
|
|
62
63
|
SCHEME_VERSION_MSG: () => SCHEME_VERSION_MSG,
|
|
63
64
|
SCHEME_VERSION_TYPED: () => SCHEME_VERSION_TYPED,
|
|
65
|
+
SCHEME_VERSION_TYPED_V2: () => SCHEME_VERSION_TYPED_V2,
|
|
64
66
|
SocketClient: () => SocketClient,
|
|
65
67
|
TYPED_DATA_LIMITS: () => TYPED_DATA_LIMITS,
|
|
66
68
|
UNRESTRICTED_METHODS: () => UNRESTRICTED_METHODS,
|
|
@@ -94,6 +96,8 @@ __export(index_exports, {
|
|
|
94
96
|
parseConnectionURI: () => parseConnectionURI,
|
|
95
97
|
qip55AddressFromBytes: () => qip55AddressFromBytes,
|
|
96
98
|
typeHash: () => typeHash,
|
|
99
|
+
typedDataSchemeTag: () => typedDataSchemeTag,
|
|
100
|
+
typedDataSchemeVersion: () => typedDataSchemeVersion,
|
|
97
101
|
verifyMessage: () => verifyMessage,
|
|
98
102
|
verifyMessageForSigner: () => verifyMessageForSigner,
|
|
99
103
|
verifyMessageSignature: () => verifyMessageSignature,
|
|
@@ -566,7 +570,7 @@ function isQrlSignedTypedDataResult(value) {
|
|
|
566
570
|
try {
|
|
567
571
|
if (!isSigningResultRecord(value)) return false;
|
|
568
572
|
const keys = hasOwn(value, "descriptor") ? TYPED_RESULT_KEYS_WITH_DESCRIPTOR : TYPED_RESULT_KEYS;
|
|
569
|
-
return hasExactOwnKeys(value, keys) && value.schemeVersion === "QRL-SIGN-TYPED-v1" && isSigningResultRecord(value.domain) && hasValidSigningFields(value);
|
|
573
|
+
return hasExactOwnKeys(value, keys) && (value.schemeVersion === "QRL-SIGN-TYPED-v1" || value.schemeVersion === "QRL-SIGN-TYPED-v2") && isSigningResultRecord(value.domain) && hasValidSigningFields(value);
|
|
570
574
|
} catch {
|
|
571
575
|
return false;
|
|
572
576
|
}
|
|
@@ -3081,8 +3085,10 @@ var ConnectionManager = _ConnectionManager;
|
|
|
3081
3085
|
// src/signing/ctx.ts
|
|
3082
3086
|
var SCHEME_VERSION_MSG = "QRL-SIGN-MSG-v1";
|
|
3083
3087
|
var SCHEME_VERSION_TYPED = "QRL-SIGN-TYPED-v1";
|
|
3088
|
+
var SCHEME_VERSION_TYPED_V2 = "QRL-SIGN-TYPED-v2";
|
|
3084
3089
|
var SCHEME_TAG_MSG = new TextEncoder().encode(SCHEME_VERSION_MSG);
|
|
3085
3090
|
var SCHEME_TAG_TYPED = new TextEncoder().encode(SCHEME_VERSION_TYPED);
|
|
3091
|
+
var SCHEME_TAG_TYPED_V2 = new TextEncoder().encode(SCHEME_VERSION_TYPED_V2);
|
|
3086
3092
|
var DIGEST_LEN = 64;
|
|
3087
3093
|
|
|
3088
3094
|
// src/signing/bytes.ts
|
|
@@ -3122,7 +3128,14 @@ function concatBytesArr(parts) {
|
|
|
3122
3128
|
}
|
|
3123
3129
|
|
|
3124
3130
|
// src/signing/typedData.ts
|
|
3125
|
-
var
|
|
3131
|
+
var SLOT_BYTES = Object.freeze({
|
|
3132
|
+
[SCHEME_VERSION_TYPED]: 32,
|
|
3133
|
+
[SCHEME_VERSION_TYPED_V2]: 64
|
|
3134
|
+
});
|
|
3135
|
+
var SCHEME_TAGS = Object.freeze({
|
|
3136
|
+
[SCHEME_VERSION_TYPED]: SCHEME_TAG_TYPED,
|
|
3137
|
+
[SCHEME_VERSION_TYPED_V2]: SCHEME_TAG_TYPED_V2
|
|
3138
|
+
});
|
|
3126
3139
|
var TYPED_DATA_LIMITS = Object.freeze({
|
|
3127
3140
|
maxTypes: 32,
|
|
3128
3141
|
maxFieldsPerType: 32,
|
|
@@ -3174,6 +3187,12 @@ function parsePayload(payload) {
|
|
|
3174
3187
|
var ATOMIC_RE = /^(?:(address|bool|string|bytes)|(u?int)(\d+)|bytes(\d+)|(.+?)\[(\d*)\])$/;
|
|
3175
3188
|
var IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
3176
3189
|
var UNSAFE_OBJECT_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
3190
|
+
function newBudget(schemeVersion) {
|
|
3191
|
+
return {
|
|
3192
|
+
remainingValues: TYPED_DATA_LIMITS.maxEncodedValues,
|
|
3193
|
+
slot: SLOT_BYTES[schemeVersion]
|
|
3194
|
+
};
|
|
3195
|
+
}
|
|
3177
3196
|
function consumeValue(budget) {
|
|
3178
3197
|
budget.remainingValues--;
|
|
3179
3198
|
if (budget.remainingValues < 0) throw new Error("typed data contains too many encoded values");
|
|
@@ -3320,29 +3339,29 @@ function encodeType(primary, types) {
|
|
|
3320
3339
|
function typeHash(primary, types) {
|
|
3321
3340
|
return shake256Digest(new TextEncoder().encode(encodeType(primary, types)), DIGEST_LEN);
|
|
3322
3341
|
}
|
|
3323
|
-
function parseQAddress(addr) {
|
|
3342
|
+
function parseQAddress(addr, slot) {
|
|
3324
3343
|
if (!isCurrentQrlAddress(addr)) {
|
|
3325
3344
|
throw new Error(`invalid Q-address: ${String(addr)}`);
|
|
3326
3345
|
}
|
|
3327
3346
|
const bytes = hexToBytes("0x" + addr.slice(1).toLowerCase());
|
|
3328
|
-
if (bytes.length >
|
|
3347
|
+
if (bytes.length > slot) {
|
|
3329
3348
|
throw new Error("qrl_signTypedData v1 does not support QIP-55 address fields");
|
|
3330
3349
|
}
|
|
3331
3350
|
return bytes;
|
|
3332
3351
|
}
|
|
3333
|
-
function
|
|
3334
|
-
if (bytes.length >
|
|
3335
|
-
const out = new Uint8Array(
|
|
3336
|
-
out.set(bytes,
|
|
3352
|
+
function padLeft(bytes, slot) {
|
|
3353
|
+
if (bytes.length > slot) throw new Error(`cannot pad: bytes > ${slot}`);
|
|
3354
|
+
const out = new Uint8Array(slot);
|
|
3355
|
+
out.set(bytes, slot - bytes.length);
|
|
3337
3356
|
return out;
|
|
3338
3357
|
}
|
|
3339
|
-
function
|
|
3340
|
-
if (bytes.length >
|
|
3341
|
-
const out = new Uint8Array(
|
|
3358
|
+
function padRight(bytes, slot) {
|
|
3359
|
+
if (bytes.length > slot) throw new Error(`cannot pad: bytes > ${slot}`);
|
|
3360
|
+
const out = new Uint8Array(slot);
|
|
3342
3361
|
out.set(bytes, 0);
|
|
3343
3362
|
return out;
|
|
3344
3363
|
}
|
|
3345
|
-
function bigIntToSlot(value, width, signed) {
|
|
3364
|
+
function bigIntToSlot(value, width, signed, slot) {
|
|
3346
3365
|
if (signed) {
|
|
3347
3366
|
const limit = 1n << BigInt(width - 1);
|
|
3348
3367
|
if (value >= limit || value < -limit) {
|
|
@@ -3353,11 +3372,12 @@ function bigIntToSlot(value, width, signed) {
|
|
|
3353
3372
|
const limit = 1n << BigInt(width);
|
|
3354
3373
|
if (value >= limit) throw new Error(`uint${width} out of range: ${value}`);
|
|
3355
3374
|
}
|
|
3356
|
-
const
|
|
3357
|
-
const
|
|
3358
|
-
const
|
|
3375
|
+
const slotBits = BigInt(slot * 8);
|
|
3376
|
+
const slotMask = (1n << slotBits) - 1n;
|
|
3377
|
+
const repr = value < 0n ? value + (1n << slotBits) & slotMask : value;
|
|
3378
|
+
const out = new Uint8Array(slot);
|
|
3359
3379
|
let v = repr;
|
|
3360
|
-
for (let i =
|
|
3380
|
+
for (let i = slot - 1; i >= 0; i--) {
|
|
3361
3381
|
out[i] = Number(v & 0xffn);
|
|
3362
3382
|
v >>= 8n;
|
|
3363
3383
|
}
|
|
@@ -3401,12 +3421,12 @@ function encodeFieldWithBudget(type, value, types, budget, depth) {
|
|
|
3401
3421
|
if (typeof value !== "string") {
|
|
3402
3422
|
throw new Error(`address field expects string: ${typeof value}`);
|
|
3403
3423
|
}
|
|
3404
|
-
return
|
|
3424
|
+
return padLeft(parseQAddress(value, budget.slot), budget.slot);
|
|
3405
3425
|
case "bool":
|
|
3406
3426
|
if (typeof value !== "boolean") {
|
|
3407
3427
|
throw new Error(`bool field expects boolean: ${typeof value}`);
|
|
3408
3428
|
}
|
|
3409
|
-
return
|
|
3429
|
+
return padLeft(new Uint8Array([value ? 1 : 0]), budget.slot);
|
|
3410
3430
|
case "string": {
|
|
3411
3431
|
if (typeof value !== "string") {
|
|
3412
3432
|
throw new Error(`string field expects string: ${typeof value}`);
|
|
@@ -3429,9 +3449,19 @@ function encodeFieldWithBudget(type, value, types, budget, depth) {
|
|
|
3429
3449
|
}
|
|
3430
3450
|
return shake256Digest(hexToBytes(value), DIGEST_LEN);
|
|
3431
3451
|
case "uintN":
|
|
3432
|
-
return bigIntToSlot(
|
|
3452
|
+
return bigIntToSlot(
|
|
3453
|
+
parseIntValue(value, `uint${parsed.width}`),
|
|
3454
|
+
parsed.width,
|
|
3455
|
+
false,
|
|
3456
|
+
budget.slot
|
|
3457
|
+
);
|
|
3433
3458
|
case "intN":
|
|
3434
|
-
return bigIntToSlot(
|
|
3459
|
+
return bigIntToSlot(
|
|
3460
|
+
parseIntValue(value, `int${parsed.width}`),
|
|
3461
|
+
parsed.width,
|
|
3462
|
+
true,
|
|
3463
|
+
budget.slot
|
|
3464
|
+
);
|
|
3435
3465
|
case "bytesN": {
|
|
3436
3466
|
if (typeof value !== "string") {
|
|
3437
3467
|
throw new Error(`bytes${parsed.width} expects 0x-hex string: ${typeof value}`);
|
|
@@ -3443,7 +3473,7 @@ function encodeFieldWithBudget(type, value, types, budget, depth) {
|
|
|
3443
3473
|
if (raw.length !== parsed.width) {
|
|
3444
3474
|
throw new Error(`bytes${parsed.width} requires ${parsed.width} bytes, got ${raw.length}`);
|
|
3445
3475
|
}
|
|
3446
|
-
return
|
|
3476
|
+
return padRight(raw, budget.slot);
|
|
3447
3477
|
}
|
|
3448
3478
|
case "array": {
|
|
3449
3479
|
if (!Array.isArray(value)) {
|
|
@@ -3462,15 +3492,10 @@ function encodeFieldWithBudget(type, value, types, budget, depth) {
|
|
|
3462
3492
|
}
|
|
3463
3493
|
}
|
|
3464
3494
|
}
|
|
3465
|
-
function encodeField(type, value, types) {
|
|
3495
|
+
function encodeField(type, value, types, schemeVersion) {
|
|
3466
3496
|
validateTypeMap(types);
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
value,
|
|
3470
|
-
types,
|
|
3471
|
-
{ remainingValues: TYPED_DATA_LIMITS.maxEncodedValues },
|
|
3472
|
-
0
|
|
3473
|
-
);
|
|
3497
|
+
const scheme = schemeVersion ?? schemeVersionForTypes(types);
|
|
3498
|
+
return encodeFieldWithBudget(type, value, types, newBudget(scheme), 0);
|
|
3474
3499
|
}
|
|
3475
3500
|
function hashStructWithBudget(primary, data, types, budget, depth) {
|
|
3476
3501
|
consumeValue(budget);
|
|
@@ -3495,15 +3520,10 @@ function hashStructWithBudget(primary, data, types, budget, depth) {
|
|
|
3495
3520
|
}
|
|
3496
3521
|
return shake256Digest(concatBytesArr(parts), DIGEST_LEN);
|
|
3497
3522
|
}
|
|
3498
|
-
function hashStruct(primary, data, types) {
|
|
3523
|
+
function hashStruct(primary, data, types, schemeVersion) {
|
|
3499
3524
|
validateTypeMap(types);
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
data,
|
|
3503
|
-
types,
|
|
3504
|
-
{ remainingValues: TYPED_DATA_LIMITS.maxEncodedValues },
|
|
3505
|
-
0
|
|
3506
|
-
);
|
|
3525
|
+
const scheme = schemeVersion ?? schemeVersionForTypes(types);
|
|
3526
|
+
return hashStructWithBudget(primary, data, types, newBudget(scheme), 0);
|
|
3507
3527
|
}
|
|
3508
3528
|
var RESERVED_DOMAIN_FIELDS = {
|
|
3509
3529
|
name: "string",
|
|
@@ -3539,7 +3559,7 @@ function validatePayloadReachability(primary, types) {
|
|
|
3539
3559
|
if (!reachable.has(k)) throw new Error(`unused referenced type: ${k}`);
|
|
3540
3560
|
}
|
|
3541
3561
|
}
|
|
3542
|
-
function
|
|
3562
|
+
function parseAndValidatePayload(payload) {
|
|
3543
3563
|
const parsed = parsePayload(payload);
|
|
3544
3564
|
validateTypeMap(parsed.types);
|
|
3545
3565
|
assertIdentifier(parsed.primaryType, "primary type");
|
|
@@ -3548,7 +3568,26 @@ function computeTypedDataDigest(payload) {
|
|
|
3548
3568
|
}
|
|
3549
3569
|
validateDomainTypes(parsed.types);
|
|
3550
3570
|
validatePayloadReachability(parsed.primaryType, parsed.types);
|
|
3551
|
-
|
|
3571
|
+
return parsed;
|
|
3572
|
+
}
|
|
3573
|
+
function schemeVersionForTypes(types) {
|
|
3574
|
+
for (const fields of Object.values(types)) {
|
|
3575
|
+
if (fields.some((f) => baseTypeName(f.type) === "address")) {
|
|
3576
|
+
return SCHEME_VERSION_TYPED_V2;
|
|
3577
|
+
}
|
|
3578
|
+
}
|
|
3579
|
+
return SCHEME_VERSION_TYPED;
|
|
3580
|
+
}
|
|
3581
|
+
function typedDataSchemeVersion(payload) {
|
|
3582
|
+
return schemeVersionForTypes(parseAndValidatePayload(payload).types);
|
|
3583
|
+
}
|
|
3584
|
+
function typedDataSchemeTag(schemeVersion) {
|
|
3585
|
+
return SCHEME_TAGS[schemeVersion];
|
|
3586
|
+
}
|
|
3587
|
+
function computeTypedDataDigest(payload) {
|
|
3588
|
+
const parsed = parseAndValidatePayload(payload);
|
|
3589
|
+
const schemeVersion = schemeVersionForTypes(parsed.types);
|
|
3590
|
+
const budget = newBudget(schemeVersion);
|
|
3552
3591
|
const domainHash = hashStructWithBudget("QRLDomain", parsed.domain, parsed.types, budget, 0);
|
|
3553
3592
|
const messageHash = hashStructWithBudget(
|
|
3554
3593
|
parsed.primaryType,
|
|
@@ -3557,7 +3596,10 @@ function computeTypedDataDigest(payload) {
|
|
|
3557
3596
|
budget,
|
|
3558
3597
|
0
|
|
3559
3598
|
);
|
|
3560
|
-
return shake256Digest(
|
|
3599
|
+
return shake256Digest(
|
|
3600
|
+
concatBytes(SCHEME_TAGS[schemeVersion], domainHash, messageHash),
|
|
3601
|
+
DIGEST_LEN
|
|
3602
|
+
);
|
|
3561
3603
|
}
|
|
3562
3604
|
|
|
3563
3605
|
// src/utils/platform.ts
|
|
@@ -3749,9 +3791,8 @@ function validateRestrictedRequest(method, params, authorizedAccounts, currentCh
|
|
|
3749
3791
|
if (params[0] !== authorizedAccount) {
|
|
3750
3792
|
throw new Error("qrl_signTypedData signer is not the authorized account");
|
|
3751
3793
|
}
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
);
|
|
3794
|
+
computeTypedDataDigest(params[1]);
|
|
3795
|
+
return void 0;
|
|
3755
3796
|
}
|
|
3756
3797
|
function requiresAuthorizedAccount(method) {
|
|
3757
3798
|
return method === "qrl_sendTransaction" || method === "qrl_signTransaction" || method === "qrl_signMessage" || method === "qrl_signTypedData";
|
|
@@ -4397,9 +4438,13 @@ function verifyMessageBytes(signature, publicKey, messageBytes) {
|
|
|
4397
4438
|
const digest = computeMessageDigest(messageBytes);
|
|
4398
4439
|
return mldsaVerify(signature, digest, publicKey, SCHEME_TAG_MSG);
|
|
4399
4440
|
}
|
|
4400
|
-
function verifyTypedDataBytes(signature, publicKey, payload) {
|
|
4441
|
+
function verifyTypedDataBytes(signature, publicKey, payload, claimedSchemeVersion) {
|
|
4442
|
+
const schemeVersion = typedDataSchemeVersion(payload);
|
|
4443
|
+
if (claimedSchemeVersion !== void 0 && claimedSchemeVersion !== schemeVersion) {
|
|
4444
|
+
return false;
|
|
4445
|
+
}
|
|
4401
4446
|
const digest = computeTypedDataDigest(payload);
|
|
4402
|
-
return mldsaVerify(signature, digest, publicKey,
|
|
4447
|
+
return mldsaVerify(signature, digest, publicKey, typedDataSchemeTag(schemeVersion));
|
|
4403
4448
|
}
|
|
4404
4449
|
function verifyMessageSignature({
|
|
4405
4450
|
signature,
|
|
@@ -4419,13 +4464,14 @@ function verifyMessageSignature({
|
|
|
4419
4464
|
function verifyTypedDataSignature({
|
|
4420
4465
|
signature,
|
|
4421
4466
|
publicKey,
|
|
4422
|
-
payload
|
|
4467
|
+
payload,
|
|
4468
|
+
schemeVersion
|
|
4423
4469
|
}) {
|
|
4424
4470
|
try {
|
|
4425
4471
|
const sig = fixedBytesOrHex(signature, ML_DSA_87_SIGNATURE_BYTES);
|
|
4426
4472
|
const pk = fixedBytesOrHex(publicKey, ML_DSA_87_PUBLIC_KEY_BYTES);
|
|
4427
4473
|
if (!sig || !pk) return false;
|
|
4428
|
-
return verifyTypedDataBytes(sig, pk, payload);
|
|
4474
|
+
return verifyTypedDataBytes(sig, pk, payload, schemeVersion);
|
|
4429
4475
|
} catch {
|
|
4430
4476
|
return false;
|
|
4431
4477
|
}
|
|
@@ -4463,7 +4509,8 @@ function verifyTypedDataForSigner({
|
|
|
4463
4509
|
descriptor,
|
|
4464
4510
|
signature,
|
|
4465
4511
|
publicKey,
|
|
4466
|
-
payload
|
|
4512
|
+
payload,
|
|
4513
|
+
schemeVersion
|
|
4467
4514
|
}) {
|
|
4468
4515
|
try {
|
|
4469
4516
|
if (descriptor === void 0) return false;
|
|
@@ -4471,7 +4518,7 @@ function verifyTypedDataForSigner({
|
|
|
4471
4518
|
const publicKeyBytes = fixedBytesOrHex(publicKey, ML_DSA_87_PUBLIC_KEY_BYTES);
|
|
4472
4519
|
const signatureBytes = fixedBytesOrHex(signature, ML_DSA_87_SIGNATURE_BYTES);
|
|
4473
4520
|
if (!descriptorBytes || !publicKeyBytes || !signatureBytes) return false;
|
|
4474
|
-
return publicKeyMatchesSigner(expectedSigner, descriptorBytes, publicKeyBytes) && verifyTypedDataBytes(signatureBytes, publicKeyBytes, payload);
|
|
4521
|
+
return publicKeyMatchesSigner(expectedSigner, descriptorBytes, publicKeyBytes) && verifyTypedDataBytes(signatureBytes, publicKeyBytes, payload, schemeVersion);
|
|
4475
4522
|
} catch {
|
|
4476
4523
|
return false;
|
|
4477
4524
|
}
|
|
@@ -4513,8 +4560,10 @@ function verifyTypedData(params) {
|
|
|
4513
4560
|
RESTRICTED_METHODS,
|
|
4514
4561
|
SCHEME_TAG_MSG,
|
|
4515
4562
|
SCHEME_TAG_TYPED,
|
|
4563
|
+
SCHEME_TAG_TYPED_V2,
|
|
4516
4564
|
SCHEME_VERSION_MSG,
|
|
4517
4565
|
SCHEME_VERSION_TYPED,
|
|
4566
|
+
SCHEME_VERSION_TYPED_V2,
|
|
4518
4567
|
SocketClient,
|
|
4519
4568
|
TYPED_DATA_LIMITS,
|
|
4520
4569
|
UNRESTRICTED_METHODS,
|
|
@@ -4548,6 +4597,8 @@ function verifyTypedData(params) {
|
|
|
4548
4597
|
parseConnectionURI,
|
|
4549
4598
|
qip55AddressFromBytes,
|
|
4550
4599
|
typeHash,
|
|
4600
|
+
typedDataSchemeTag,
|
|
4601
|
+
typedDataSchemeVersion,
|
|
4551
4602
|
verifyMessage,
|
|
4552
4603
|
verifyMessageForSigner,
|
|
4553
4604
|
verifyMessageSignature,
|
package/dist/index.mjs
CHANGED
|
@@ -461,7 +461,7 @@ function isQrlSignedTypedDataResult(value) {
|
|
|
461
461
|
try {
|
|
462
462
|
if (!isSigningResultRecord(value)) return false;
|
|
463
463
|
const keys = hasOwn(value, "descriptor") ? TYPED_RESULT_KEYS_WITH_DESCRIPTOR : TYPED_RESULT_KEYS;
|
|
464
|
-
return hasExactOwnKeys(value, keys) && value.schemeVersion === "QRL-SIGN-TYPED-v1" && isSigningResultRecord(value.domain) && hasValidSigningFields(value);
|
|
464
|
+
return hasExactOwnKeys(value, keys) && (value.schemeVersion === "QRL-SIGN-TYPED-v1" || value.schemeVersion === "QRL-SIGN-TYPED-v2") && isSigningResultRecord(value.domain) && hasValidSigningFields(value);
|
|
465
465
|
} catch {
|
|
466
466
|
return false;
|
|
467
467
|
}
|
|
@@ -2976,8 +2976,10 @@ var ConnectionManager = _ConnectionManager;
|
|
|
2976
2976
|
// src/signing/ctx.ts
|
|
2977
2977
|
var SCHEME_VERSION_MSG = "QRL-SIGN-MSG-v1";
|
|
2978
2978
|
var SCHEME_VERSION_TYPED = "QRL-SIGN-TYPED-v1";
|
|
2979
|
+
var SCHEME_VERSION_TYPED_V2 = "QRL-SIGN-TYPED-v2";
|
|
2979
2980
|
var SCHEME_TAG_MSG = new TextEncoder().encode(SCHEME_VERSION_MSG);
|
|
2980
2981
|
var SCHEME_TAG_TYPED = new TextEncoder().encode(SCHEME_VERSION_TYPED);
|
|
2982
|
+
var SCHEME_TAG_TYPED_V2 = new TextEncoder().encode(SCHEME_VERSION_TYPED_V2);
|
|
2981
2983
|
var DIGEST_LEN = 64;
|
|
2982
2984
|
|
|
2983
2985
|
// src/signing/bytes.ts
|
|
@@ -3017,7 +3019,14 @@ function concatBytesArr(parts) {
|
|
|
3017
3019
|
}
|
|
3018
3020
|
|
|
3019
3021
|
// src/signing/typedData.ts
|
|
3020
|
-
var
|
|
3022
|
+
var SLOT_BYTES = Object.freeze({
|
|
3023
|
+
[SCHEME_VERSION_TYPED]: 32,
|
|
3024
|
+
[SCHEME_VERSION_TYPED_V2]: 64
|
|
3025
|
+
});
|
|
3026
|
+
var SCHEME_TAGS = Object.freeze({
|
|
3027
|
+
[SCHEME_VERSION_TYPED]: SCHEME_TAG_TYPED,
|
|
3028
|
+
[SCHEME_VERSION_TYPED_V2]: SCHEME_TAG_TYPED_V2
|
|
3029
|
+
});
|
|
3021
3030
|
var TYPED_DATA_LIMITS = Object.freeze({
|
|
3022
3031
|
maxTypes: 32,
|
|
3023
3032
|
maxFieldsPerType: 32,
|
|
@@ -3069,6 +3078,12 @@ function parsePayload(payload) {
|
|
|
3069
3078
|
var ATOMIC_RE = /^(?:(address|bool|string|bytes)|(u?int)(\d+)|bytes(\d+)|(.+?)\[(\d*)\])$/;
|
|
3070
3079
|
var IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
3071
3080
|
var UNSAFE_OBJECT_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
3081
|
+
function newBudget(schemeVersion) {
|
|
3082
|
+
return {
|
|
3083
|
+
remainingValues: TYPED_DATA_LIMITS.maxEncodedValues,
|
|
3084
|
+
slot: SLOT_BYTES[schemeVersion]
|
|
3085
|
+
};
|
|
3086
|
+
}
|
|
3072
3087
|
function consumeValue(budget) {
|
|
3073
3088
|
budget.remainingValues--;
|
|
3074
3089
|
if (budget.remainingValues < 0) throw new Error("typed data contains too many encoded values");
|
|
@@ -3215,29 +3230,29 @@ function encodeType(primary, types) {
|
|
|
3215
3230
|
function typeHash(primary, types) {
|
|
3216
3231
|
return shake256Digest(new TextEncoder().encode(encodeType(primary, types)), DIGEST_LEN);
|
|
3217
3232
|
}
|
|
3218
|
-
function parseQAddress(addr) {
|
|
3233
|
+
function parseQAddress(addr, slot) {
|
|
3219
3234
|
if (!isCurrentQrlAddress(addr)) {
|
|
3220
3235
|
throw new Error(`invalid Q-address: ${String(addr)}`);
|
|
3221
3236
|
}
|
|
3222
3237
|
const bytes = hexToBytes("0x" + addr.slice(1).toLowerCase());
|
|
3223
|
-
if (bytes.length >
|
|
3238
|
+
if (bytes.length > slot) {
|
|
3224
3239
|
throw new Error("qrl_signTypedData v1 does not support QIP-55 address fields");
|
|
3225
3240
|
}
|
|
3226
3241
|
return bytes;
|
|
3227
3242
|
}
|
|
3228
|
-
function
|
|
3229
|
-
if (bytes.length >
|
|
3230
|
-
const out = new Uint8Array(
|
|
3231
|
-
out.set(bytes,
|
|
3243
|
+
function padLeft(bytes, slot) {
|
|
3244
|
+
if (bytes.length > slot) throw new Error(`cannot pad: bytes > ${slot}`);
|
|
3245
|
+
const out = new Uint8Array(slot);
|
|
3246
|
+
out.set(bytes, slot - bytes.length);
|
|
3232
3247
|
return out;
|
|
3233
3248
|
}
|
|
3234
|
-
function
|
|
3235
|
-
if (bytes.length >
|
|
3236
|
-
const out = new Uint8Array(
|
|
3249
|
+
function padRight(bytes, slot) {
|
|
3250
|
+
if (bytes.length > slot) throw new Error(`cannot pad: bytes > ${slot}`);
|
|
3251
|
+
const out = new Uint8Array(slot);
|
|
3237
3252
|
out.set(bytes, 0);
|
|
3238
3253
|
return out;
|
|
3239
3254
|
}
|
|
3240
|
-
function bigIntToSlot(value, width, signed) {
|
|
3255
|
+
function bigIntToSlot(value, width, signed, slot) {
|
|
3241
3256
|
if (signed) {
|
|
3242
3257
|
const limit = 1n << BigInt(width - 1);
|
|
3243
3258
|
if (value >= limit || value < -limit) {
|
|
@@ -3248,11 +3263,12 @@ function bigIntToSlot(value, width, signed) {
|
|
|
3248
3263
|
const limit = 1n << BigInt(width);
|
|
3249
3264
|
if (value >= limit) throw new Error(`uint${width} out of range: ${value}`);
|
|
3250
3265
|
}
|
|
3251
|
-
const
|
|
3252
|
-
const
|
|
3253
|
-
const
|
|
3266
|
+
const slotBits = BigInt(slot * 8);
|
|
3267
|
+
const slotMask = (1n << slotBits) - 1n;
|
|
3268
|
+
const repr = value < 0n ? value + (1n << slotBits) & slotMask : value;
|
|
3269
|
+
const out = new Uint8Array(slot);
|
|
3254
3270
|
let v = repr;
|
|
3255
|
-
for (let i =
|
|
3271
|
+
for (let i = slot - 1; i >= 0; i--) {
|
|
3256
3272
|
out[i] = Number(v & 0xffn);
|
|
3257
3273
|
v >>= 8n;
|
|
3258
3274
|
}
|
|
@@ -3296,12 +3312,12 @@ function encodeFieldWithBudget(type, value, types, budget, depth) {
|
|
|
3296
3312
|
if (typeof value !== "string") {
|
|
3297
3313
|
throw new Error(`address field expects string: ${typeof value}`);
|
|
3298
3314
|
}
|
|
3299
|
-
return
|
|
3315
|
+
return padLeft(parseQAddress(value, budget.slot), budget.slot);
|
|
3300
3316
|
case "bool":
|
|
3301
3317
|
if (typeof value !== "boolean") {
|
|
3302
3318
|
throw new Error(`bool field expects boolean: ${typeof value}`);
|
|
3303
3319
|
}
|
|
3304
|
-
return
|
|
3320
|
+
return padLeft(new Uint8Array([value ? 1 : 0]), budget.slot);
|
|
3305
3321
|
case "string": {
|
|
3306
3322
|
if (typeof value !== "string") {
|
|
3307
3323
|
throw new Error(`string field expects string: ${typeof value}`);
|
|
@@ -3324,9 +3340,19 @@ function encodeFieldWithBudget(type, value, types, budget, depth) {
|
|
|
3324
3340
|
}
|
|
3325
3341
|
return shake256Digest(hexToBytes(value), DIGEST_LEN);
|
|
3326
3342
|
case "uintN":
|
|
3327
|
-
return bigIntToSlot(
|
|
3343
|
+
return bigIntToSlot(
|
|
3344
|
+
parseIntValue(value, `uint${parsed.width}`),
|
|
3345
|
+
parsed.width,
|
|
3346
|
+
false,
|
|
3347
|
+
budget.slot
|
|
3348
|
+
);
|
|
3328
3349
|
case "intN":
|
|
3329
|
-
return bigIntToSlot(
|
|
3350
|
+
return bigIntToSlot(
|
|
3351
|
+
parseIntValue(value, `int${parsed.width}`),
|
|
3352
|
+
parsed.width,
|
|
3353
|
+
true,
|
|
3354
|
+
budget.slot
|
|
3355
|
+
);
|
|
3330
3356
|
case "bytesN": {
|
|
3331
3357
|
if (typeof value !== "string") {
|
|
3332
3358
|
throw new Error(`bytes${parsed.width} expects 0x-hex string: ${typeof value}`);
|
|
@@ -3338,7 +3364,7 @@ function encodeFieldWithBudget(type, value, types, budget, depth) {
|
|
|
3338
3364
|
if (raw.length !== parsed.width) {
|
|
3339
3365
|
throw new Error(`bytes${parsed.width} requires ${parsed.width} bytes, got ${raw.length}`);
|
|
3340
3366
|
}
|
|
3341
|
-
return
|
|
3367
|
+
return padRight(raw, budget.slot);
|
|
3342
3368
|
}
|
|
3343
3369
|
case "array": {
|
|
3344
3370
|
if (!Array.isArray(value)) {
|
|
@@ -3357,15 +3383,10 @@ function encodeFieldWithBudget(type, value, types, budget, depth) {
|
|
|
3357
3383
|
}
|
|
3358
3384
|
}
|
|
3359
3385
|
}
|
|
3360
|
-
function encodeField(type, value, types) {
|
|
3386
|
+
function encodeField(type, value, types, schemeVersion) {
|
|
3361
3387
|
validateTypeMap(types);
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
value,
|
|
3365
|
-
types,
|
|
3366
|
-
{ remainingValues: TYPED_DATA_LIMITS.maxEncodedValues },
|
|
3367
|
-
0
|
|
3368
|
-
);
|
|
3388
|
+
const scheme = schemeVersion ?? schemeVersionForTypes(types);
|
|
3389
|
+
return encodeFieldWithBudget(type, value, types, newBudget(scheme), 0);
|
|
3369
3390
|
}
|
|
3370
3391
|
function hashStructWithBudget(primary, data, types, budget, depth) {
|
|
3371
3392
|
consumeValue(budget);
|
|
@@ -3390,15 +3411,10 @@ function hashStructWithBudget(primary, data, types, budget, depth) {
|
|
|
3390
3411
|
}
|
|
3391
3412
|
return shake256Digest(concatBytesArr(parts), DIGEST_LEN);
|
|
3392
3413
|
}
|
|
3393
|
-
function hashStruct(primary, data, types) {
|
|
3414
|
+
function hashStruct(primary, data, types, schemeVersion) {
|
|
3394
3415
|
validateTypeMap(types);
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
data,
|
|
3398
|
-
types,
|
|
3399
|
-
{ remainingValues: TYPED_DATA_LIMITS.maxEncodedValues },
|
|
3400
|
-
0
|
|
3401
|
-
);
|
|
3416
|
+
const scheme = schemeVersion ?? schemeVersionForTypes(types);
|
|
3417
|
+
return hashStructWithBudget(primary, data, types, newBudget(scheme), 0);
|
|
3402
3418
|
}
|
|
3403
3419
|
var RESERVED_DOMAIN_FIELDS = {
|
|
3404
3420
|
name: "string",
|
|
@@ -3434,7 +3450,7 @@ function validatePayloadReachability(primary, types) {
|
|
|
3434
3450
|
if (!reachable.has(k)) throw new Error(`unused referenced type: ${k}`);
|
|
3435
3451
|
}
|
|
3436
3452
|
}
|
|
3437
|
-
function
|
|
3453
|
+
function parseAndValidatePayload(payload) {
|
|
3438
3454
|
const parsed = parsePayload(payload);
|
|
3439
3455
|
validateTypeMap(parsed.types);
|
|
3440
3456
|
assertIdentifier(parsed.primaryType, "primary type");
|
|
@@ -3443,7 +3459,26 @@ function computeTypedDataDigest(payload) {
|
|
|
3443
3459
|
}
|
|
3444
3460
|
validateDomainTypes(parsed.types);
|
|
3445
3461
|
validatePayloadReachability(parsed.primaryType, parsed.types);
|
|
3446
|
-
|
|
3462
|
+
return parsed;
|
|
3463
|
+
}
|
|
3464
|
+
function schemeVersionForTypes(types) {
|
|
3465
|
+
for (const fields of Object.values(types)) {
|
|
3466
|
+
if (fields.some((f) => baseTypeName(f.type) === "address")) {
|
|
3467
|
+
return SCHEME_VERSION_TYPED_V2;
|
|
3468
|
+
}
|
|
3469
|
+
}
|
|
3470
|
+
return SCHEME_VERSION_TYPED;
|
|
3471
|
+
}
|
|
3472
|
+
function typedDataSchemeVersion(payload) {
|
|
3473
|
+
return schemeVersionForTypes(parseAndValidatePayload(payload).types);
|
|
3474
|
+
}
|
|
3475
|
+
function typedDataSchemeTag(schemeVersion) {
|
|
3476
|
+
return SCHEME_TAGS[schemeVersion];
|
|
3477
|
+
}
|
|
3478
|
+
function computeTypedDataDigest(payload) {
|
|
3479
|
+
const parsed = parseAndValidatePayload(payload);
|
|
3480
|
+
const schemeVersion = schemeVersionForTypes(parsed.types);
|
|
3481
|
+
const budget = newBudget(schemeVersion);
|
|
3447
3482
|
const domainHash = hashStructWithBudget("QRLDomain", parsed.domain, parsed.types, budget, 0);
|
|
3448
3483
|
const messageHash = hashStructWithBudget(
|
|
3449
3484
|
parsed.primaryType,
|
|
@@ -3452,7 +3487,10 @@ function computeTypedDataDigest(payload) {
|
|
|
3452
3487
|
budget,
|
|
3453
3488
|
0
|
|
3454
3489
|
);
|
|
3455
|
-
return shake256Digest(
|
|
3490
|
+
return shake256Digest(
|
|
3491
|
+
concatBytes(SCHEME_TAGS[schemeVersion], domainHash, messageHash),
|
|
3492
|
+
DIGEST_LEN
|
|
3493
|
+
);
|
|
3456
3494
|
}
|
|
3457
3495
|
|
|
3458
3496
|
// src/utils/platform.ts
|
|
@@ -3644,9 +3682,8 @@ function validateRestrictedRequest(method, params, authorizedAccounts, currentCh
|
|
|
3644
3682
|
if (params[0] !== authorizedAccount) {
|
|
3645
3683
|
throw new Error("qrl_signTypedData signer is not the authorized account");
|
|
3646
3684
|
}
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
);
|
|
3685
|
+
computeTypedDataDigest(params[1]);
|
|
3686
|
+
return void 0;
|
|
3650
3687
|
}
|
|
3651
3688
|
function requiresAuthorizedAccount(method) {
|
|
3652
3689
|
return method === "qrl_sendTransaction" || method === "qrl_signTransaction" || method === "qrl_signMessage" || method === "qrl_signTypedData";
|
|
@@ -4292,9 +4329,13 @@ function verifyMessageBytes(signature, publicKey, messageBytes) {
|
|
|
4292
4329
|
const digest = computeMessageDigest(messageBytes);
|
|
4293
4330
|
return mldsaVerify(signature, digest, publicKey, SCHEME_TAG_MSG);
|
|
4294
4331
|
}
|
|
4295
|
-
function verifyTypedDataBytes(signature, publicKey, payload) {
|
|
4332
|
+
function verifyTypedDataBytes(signature, publicKey, payload, claimedSchemeVersion) {
|
|
4333
|
+
const schemeVersion = typedDataSchemeVersion(payload);
|
|
4334
|
+
if (claimedSchemeVersion !== void 0 && claimedSchemeVersion !== schemeVersion) {
|
|
4335
|
+
return false;
|
|
4336
|
+
}
|
|
4296
4337
|
const digest = computeTypedDataDigest(payload);
|
|
4297
|
-
return mldsaVerify(signature, digest, publicKey,
|
|
4338
|
+
return mldsaVerify(signature, digest, publicKey, typedDataSchemeTag(schemeVersion));
|
|
4298
4339
|
}
|
|
4299
4340
|
function verifyMessageSignature({
|
|
4300
4341
|
signature,
|
|
@@ -4314,13 +4355,14 @@ function verifyMessageSignature({
|
|
|
4314
4355
|
function verifyTypedDataSignature({
|
|
4315
4356
|
signature,
|
|
4316
4357
|
publicKey,
|
|
4317
|
-
payload
|
|
4358
|
+
payload,
|
|
4359
|
+
schemeVersion
|
|
4318
4360
|
}) {
|
|
4319
4361
|
try {
|
|
4320
4362
|
const sig = fixedBytesOrHex(signature, ML_DSA_87_SIGNATURE_BYTES);
|
|
4321
4363
|
const pk = fixedBytesOrHex(publicKey, ML_DSA_87_PUBLIC_KEY_BYTES);
|
|
4322
4364
|
if (!sig || !pk) return false;
|
|
4323
|
-
return verifyTypedDataBytes(sig, pk, payload);
|
|
4365
|
+
return verifyTypedDataBytes(sig, pk, payload, schemeVersion);
|
|
4324
4366
|
} catch {
|
|
4325
4367
|
return false;
|
|
4326
4368
|
}
|
|
@@ -4358,7 +4400,8 @@ function verifyTypedDataForSigner({
|
|
|
4358
4400
|
descriptor,
|
|
4359
4401
|
signature,
|
|
4360
4402
|
publicKey,
|
|
4361
|
-
payload
|
|
4403
|
+
payload,
|
|
4404
|
+
schemeVersion
|
|
4362
4405
|
}) {
|
|
4363
4406
|
try {
|
|
4364
4407
|
if (descriptor === void 0) return false;
|
|
@@ -4366,7 +4409,7 @@ function verifyTypedDataForSigner({
|
|
|
4366
4409
|
const publicKeyBytes = fixedBytesOrHex(publicKey, ML_DSA_87_PUBLIC_KEY_BYTES);
|
|
4367
4410
|
const signatureBytes = fixedBytesOrHex(signature, ML_DSA_87_SIGNATURE_BYTES);
|
|
4368
4411
|
if (!descriptorBytes || !publicKeyBytes || !signatureBytes) return false;
|
|
4369
|
-
return publicKeyMatchesSigner(expectedSigner, descriptorBytes, publicKeyBytes) && verifyTypedDataBytes(signatureBytes, publicKeyBytes, payload);
|
|
4412
|
+
return publicKeyMatchesSigner(expectedSigner, descriptorBytes, publicKeyBytes) && verifyTypedDataBytes(signatureBytes, publicKeyBytes, payload, schemeVersion);
|
|
4370
4413
|
} catch {
|
|
4371
4414
|
return false;
|
|
4372
4415
|
}
|
|
@@ -4407,8 +4450,10 @@ export {
|
|
|
4407
4450
|
RESTRICTED_METHODS,
|
|
4408
4451
|
SCHEME_TAG_MSG,
|
|
4409
4452
|
SCHEME_TAG_TYPED,
|
|
4453
|
+
SCHEME_TAG_TYPED_V2,
|
|
4410
4454
|
SCHEME_VERSION_MSG,
|
|
4411
4455
|
SCHEME_VERSION_TYPED,
|
|
4456
|
+
SCHEME_VERSION_TYPED_V2,
|
|
4412
4457
|
SocketClient,
|
|
4413
4458
|
TYPED_DATA_LIMITS,
|
|
4414
4459
|
UNRESTRICTED_METHODS,
|
|
@@ -4442,6 +4487,8 @@ export {
|
|
|
4442
4487
|
parseConnectionURI,
|
|
4443
4488
|
qip55AddressFromBytes,
|
|
4444
4489
|
typeHash,
|
|
4490
|
+
typedDataSchemeTag,
|
|
4491
|
+
typedDataSchemeVersion,
|
|
4445
4492
|
verifyMessage,
|
|
4446
4493
|
verifyMessageForSigner,
|
|
4447
4494
|
verifyMessageSignature,
|