@neuraiproject/neurai-message 0.8.1 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export * from "./core";
@@ -0,0 +1,6 @@
1
+ /** returns a base64 encoded string representation of the legacy signature */
2
+ export declare function sign(message: string, privateKey: Uint8Array, compressed?: boolean): string;
3
+ export declare function signPQMessage(message: string, privateKey: Uint8Array, publicKey: Uint8Array): string;
4
+ export declare function verifyLegacyMessage(message: string, address: string, signature: string | Uint8Array): any;
5
+ export declare function verifyPQMessage(message: string, address: string, signature: string | Uint8Array): any;
6
+ export declare function verifyMessage(message: string, address: string, signature: string | Uint8Array): any;
@@ -0,0 +1,8 @@
1
+ import * as api from "./browser";
2
+ declare global {
3
+ interface Window {
4
+ NeuraiMessage: typeof api;
5
+ }
6
+ }
7
+ export * from "./browser";
8
+ export default api;
@@ -0,0 +1 @@
1
+ export * from "./core";
@@ -0,0 +1,6 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ import { Buffer } from "buffer";
4
+ export declare function magicHash(message: string | Buffer, messagePrefix: string | Buffer): any;
5
+ export declare function signLegacyMessage(message: string, privateKey: Uint8Array, compressed: boolean, messagePrefix: string | Buffer): Buffer;
6
+ export declare function verifyLegacyCompactMessage(message: string, address: string, signature: Uint8Array, messagePrefix: string | Buffer): any;
package/index.ts CHANGED
@@ -1,256 +1 @@
1
- import { createHash } from "crypto";
2
- import { bech32m } from "bech32";
3
- import * as bitcoinMessage from "bitcoinjs-message";
4
- import { ml_dsa44 } from "@noble/post-quantum/ml-dsa.js";
5
-
6
- const MESSAGE_MAGIC = "Neurai Signed Message:\n";
7
- const PQ_MESSAGE_SIGNATURE_PREFIX = 0x35;
8
- const PQ_SERIALIZED_PUBKEY_PREFIX = 0x05;
9
- const PQ_PUBLIC_KEY_LENGTH = 1312;
10
- const PQ_SERIALIZED_PUBKEY_LENGTH = 1 + PQ_PUBLIC_KEY_LENGTH;
11
- const PQ_SIGNATURE_LENGTH = 2420;
12
- const LEGACY_MESSAGE_PREFIX =
13
- String.fromCharCode(Buffer.byteLength(MESSAGE_MAGIC, "utf8")) +
14
- MESSAGE_MAGIC;
15
-
16
- function encodeCompactSize(value: number): Buffer {
17
- if (!Number.isInteger(value) || value < 0) {
18
- throw new Error("CompactSize value must be a non-negative integer");
19
- }
20
-
21
- if (value < 253) {
22
- return Buffer.from([value]);
23
- }
24
-
25
- if (value <= 0xffff) {
26
- const buffer = Buffer.alloc(3);
27
- buffer[0] = 0xfd;
28
- buffer.writeUInt16LE(value, 1);
29
- return buffer;
30
- }
31
-
32
- if (value <= 0xffffffff) {
33
- const buffer = Buffer.alloc(5);
34
- buffer[0] = 0xfe;
35
- buffer.writeUInt32LE(value, 1);
36
- return buffer;
37
- }
38
-
39
- throw new Error("CompactSize values above uint32 are not supported");
40
- }
41
-
42
- function decodeCompactSize(buffer: Buffer, offset: number) {
43
- if (offset >= buffer.length) {
44
- throw new Error("Unexpected end of CompactSize data");
45
- }
46
-
47
- const first = buffer[offset];
48
- if (first < 253) {
49
- return { value: first, offset: offset + 1 };
50
- }
51
-
52
- if (first === 0xfd) {
53
- if (offset + 3 > buffer.length) {
54
- throw new Error("Unexpected end of CompactSize uint16 data");
55
- }
56
- return { value: buffer.readUInt16LE(offset + 1), offset: offset + 3 };
57
- }
58
-
59
- if (first === 0xfe) {
60
- if (offset + 5 > buffer.length) {
61
- throw new Error("Unexpected end of CompactSize uint32 data");
62
- }
63
- return { value: buffer.readUInt32LE(offset + 1), offset: offset + 5 };
64
- }
65
-
66
- if (first === 0xff) {
67
- throw new Error("CompactSize uint64 is not supported");
68
- }
69
-
70
- throw new Error("Invalid CompactSize prefix");
71
- }
72
-
73
- function sha256(bytes: Uint8Array) {
74
- return createHash("sha256").update(bytes).digest();
75
- }
76
-
77
- function hash256(bytes: Uint8Array) {
78
- return sha256(sha256(bytes));
79
- }
80
-
81
- function hash160(bytes: Uint8Array) {
82
- return createHash("ripemd160").update(sha256(bytes)).digest();
83
- }
84
-
85
- function encodeMessageHash(message: string) {
86
- const messageBytes = Buffer.from(message, "utf8");
87
- const magicBytes = Buffer.from(MESSAGE_MAGIC, "utf8");
88
- const payload = Buffer.concat([
89
- encodeCompactSize(magicBytes.length),
90
- magicBytes,
91
- encodeCompactSize(messageBytes.length),
92
- messageBytes,
93
- ]);
94
-
95
- return hash256(payload);
96
- }
97
-
98
- function toSignatureBuffer(signature: string | Uint8Array) {
99
- return typeof signature === "string"
100
- ? Buffer.from(signature, "base64")
101
- : Buffer.from(signature);
102
- }
103
-
104
- function normalizePQPublicKey(publicKey: Uint8Array) {
105
- const buffer = Buffer.from(publicKey);
106
-
107
- if (
108
- buffer.length === PQ_SERIALIZED_PUBKEY_LENGTH &&
109
- buffer[0] === PQ_SERIALIZED_PUBKEY_PREFIX
110
- ) {
111
- return buffer;
112
- }
113
-
114
- if (buffer.length === PQ_PUBLIC_KEY_LENGTH) {
115
- return Buffer.concat([Buffer.from([PQ_SERIALIZED_PUBKEY_PREFIX]), buffer]);
116
- }
117
-
118
- throw new Error("Invalid PQ public key length");
119
- }
120
-
121
- function isPQMessageSignature(signature: string | Uint8Array) {
122
- const buffer = toSignatureBuffer(signature);
123
- return buffer.length > 0 && buffer[0] === PQ_MESSAGE_SIGNATURE_PREFIX;
124
- }
125
-
126
- function decodePQAddress(address: string) {
127
- const decoded = bech32m.decode(address);
128
- if (decoded.words.length === 0) {
129
- throw new Error("Invalid bech32m address");
130
- }
131
-
132
- return {
133
- prefix: decoded.prefix,
134
- version: decoded.words[0],
135
- program: Buffer.from(bech32m.fromWords(decoded.words.slice(1))),
136
- };
137
- }
138
-
139
- /** returns a base64 encoded string representation of the legacy signature */
140
- export function sign(message: string, privateKey: Uint8Array, compressed = true) {
141
- const signature = bitcoinMessage.sign(
142
- message,
143
- Buffer.from(privateKey),
144
- compressed,
145
- LEGACY_MESSAGE_PREFIX
146
- );
147
-
148
- return signature.toString("base64");
149
- }
150
-
151
- export function signPQMessage(
152
- message: string,
153
- privateKey: Uint8Array,
154
- publicKey: Uint8Array
155
- ) {
156
- const serializedPublicKey = normalizePQPublicKey(publicKey);
157
- const hash = encodeMessageHash(message);
158
- const pqSignature = Buffer.from(ml_dsa44.sign(hash, Buffer.from(privateKey)));
159
-
160
- const payload = Buffer.concat([
161
- Buffer.from([PQ_MESSAGE_SIGNATURE_PREFIX]),
162
- encodeCompactSize(serializedPublicKey.length),
163
- serializedPublicKey,
164
- encodeCompactSize(pqSignature.length),
165
- pqSignature,
166
- ]);
167
-
168
- return payload.toString("base64");
169
- }
170
-
171
- export function verifyLegacyMessage(
172
- message: string,
173
- address: string,
174
- signature: string | Uint8Array
175
- ) {
176
- try {
177
- return bitcoinMessage.verify(
178
- message,
179
- address,
180
- toSignatureBuffer(signature),
181
- LEGACY_MESSAGE_PREFIX
182
- );
183
- } catch {
184
- return false;
185
- }
186
- }
187
-
188
- export function verifyPQMessage(
189
- message: string,
190
- address: string,
191
- signature: string | Uint8Array
192
- ) {
193
- try {
194
- const payload = toSignatureBuffer(signature);
195
- let offset = 0;
196
-
197
- if (payload[offset++] !== PQ_MESSAGE_SIGNATURE_PREFIX) {
198
- return false;
199
- }
200
-
201
- const publicKeyLength = decodeCompactSize(payload, offset);
202
- offset = publicKeyLength.offset;
203
-
204
- const serializedPublicKey = payload.subarray(
205
- offset,
206
- offset + publicKeyLength.value
207
- );
208
- offset += publicKeyLength.value;
209
-
210
- const signatureLength = decodeCompactSize(payload, offset);
211
- offset = signatureLength.offset;
212
-
213
- const pqSignature = payload.subarray(offset, offset + signatureLength.value);
214
- offset += signatureLength.value;
215
-
216
- if (offset !== payload.length) {
217
- return false;
218
- }
219
-
220
- if (
221
- serializedPublicKey.length !== PQ_SERIALIZED_PUBKEY_LENGTH ||
222
- serializedPublicKey[0] !== PQ_SERIALIZED_PUBKEY_PREFIX ||
223
- pqSignature.length !== PQ_SIGNATURE_LENGTH
224
- ) {
225
- return false;
226
- }
227
-
228
- const decodedAddress = decodePQAddress(address);
229
- if (decodedAddress.version !== 1 || decodedAddress.program.length !== 20) {
230
- return false;
231
- }
232
-
233
- const expectedProgram = hash160(serializedPublicKey);
234
- if (!expectedProgram.equals(decodedAddress.program)) {
235
- return false;
236
- }
237
-
238
- return ml_dsa44.verify(
239
- pqSignature,
240
- encodeMessageHash(message),
241
- serializedPublicKey.subarray(1)
242
- );
243
- } catch {
244
- return false;
245
- }
246
- }
247
-
248
- export function verifyMessage(
249
- message: string,
250
- address: string,
251
- signature: string | Uint8Array
252
- ) {
253
- return isPQMessageSignature(signature)
254
- ? verifyPQMessage(message, address, signature)
255
- : verifyLegacyMessage(message, address, signature);
256
- }
1
+ export * from "./src/index";
package/package.json CHANGED
@@ -1,14 +1,31 @@
1
1
  {
2
2
  "name": "@neuraiproject/neurai-message",
3
- "version": "0.8.1",
3
+ "version": "0.9.1",
4
4
  "description": "Sign and Verify messages in Neurai",
5
5
  "source": "index.ts",
6
- "main": "dist/main.js",
7
- "module": "dist/module.js",
6
+ "main": "dist/index.cjs",
7
+ "module": "dist/index.mjs",
8
8
  "types": "dist/index.d.ts",
9
+ "browser": {
10
+ "stream": "stream-browserify",
11
+ "buffer": "buffer/",
12
+ "process": "process/browser"
13
+ },
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.mjs",
18
+ "require": "./dist/index.cjs"
19
+ },
20
+ "./browser": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/browser.mjs"
23
+ },
24
+ "./global": "./dist/NeuraiMessage.global.js"
25
+ },
9
26
  "scripts": {
10
- "test": "npm run build && node --experimental-vm-modules ./node_modules/jest/bin/jest.js",
11
- "build": "esbuild index.ts --bundle --platform=node --target=es2020 --format=cjs --outfile=dist/main.js --external:crypto --external:buffer --external:stream --external:string_decoder --external:events && esbuild index.ts --bundle --platform=node --target=es2020 --format=esm --outfile=dist/module.js --external:crypto --external:buffer --external:stream --external:string_decoder --external:events && tsc --emitDeclarationOnly --declaration --outDir dist"
27
+ "test": "npm run build && vitest run",
28
+ "build": "esbuild src/index.ts --bundle --platform=node --target=es2020 --format=cjs --outfile=dist/index.cjs && esbuild src/index.ts --bundle --platform=node --target=es2020 --format=esm --outfile=dist/index.mjs && esbuild src/browser.ts --bundle --platform=browser --target=es2020 --format=esm --main-fields=browser,module,main --outfile=dist/browser.mjs --inject:./src/browser-shims.ts && esbuild src/global.ts --bundle --platform=browser --target=es2020 --format=iife --global-name=NeuraiMessage --main-fields=browser,module,main --outfile=dist/NeuraiMessage.global.js --inject:./src/browser-shims.ts && tsc --emitDeclarationOnly --declaration --outDir dist"
12
29
  },
13
30
  "repository": {
14
31
  "type": "git",
@@ -25,15 +42,22 @@
25
42
  },
26
43
  "homepage": "https://github.com/neuraiproject/neurai-message#readme",
27
44
  "dependencies": {
45
+ "@noble/hashes": "^2.0.1",
28
46
  "@noble/post-quantum": "^0.6.0",
47
+ "@noble/secp256k1": "^3.0.0",
29
48
  "bech32": "^2.0.0",
30
- "bitcoinjs-message": "^2.2.0"
49
+ "bs58check": "^2.1.2",
50
+ "create-hash": "^1.2.0",
51
+ "varuint-bitcoin": "^1.1.2"
31
52
  },
32
53
  "devDependencies": {
33
- "@types/jest": "^28.1.4",
34
54
  "@types/node": "^18.0.0",
55
+ "buffer": "^6.0.3",
35
56
  "esbuild": "^0.25.0",
36
- "jest": "^29.4.0",
37
- "typescript": "^4.9.4"
57
+ "events": "^3.3.0",
58
+ "process": "^0.11.10",
59
+ "stream-browserify": "^3.0.0",
60
+ "typescript": "^4.9.4",
61
+ "vitest": "^3.2.4"
38
62
  }
39
63
  }
@@ -0,0 +1,9 @@
1
+ import { Buffer } from "buffer";
2
+ import process from "process/browser";
3
+
4
+ if (typeof globalThis !== "undefined") {
5
+ (globalThis as typeof globalThis & { Buffer?: typeof Buffer }).Buffer ??=
6
+ Buffer;
7
+ (globalThis as typeof globalThis & { process?: typeof process }).process ??=
8
+ process;
9
+ }
package/src/browser.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./core";
package/src/core.ts ADDED
@@ -0,0 +1,287 @@
1
+ import { Buffer } from "buffer";
2
+ import createHash from "create-hash";
3
+ import { bech32m } from "bech32";
4
+ import { ml_dsa44 } from "@noble/post-quantum/ml-dsa.js";
5
+ import { signLegacyMessage, verifyLegacyCompactMessage } from "./legacy-message";
6
+
7
+ const MESSAGE_MAGIC = "Neurai Signed Message:\n";
8
+ const PQ_MESSAGE_SIGNATURE_PREFIX = 0x35;
9
+ const PQ_SERIALIZED_PUBKEY_PREFIX = 0x05;
10
+ const PQ_PUBLIC_KEY_LENGTH = 1312;
11
+ const PQ_SERIALIZED_PUBKEY_LENGTH = 1 + PQ_PUBLIC_KEY_LENGTH;
12
+ const PQ_SIGNATURE_LENGTH = 2420;
13
+ const AUTHSCRIPT_PROGRAM_LENGTH = 32;
14
+ const AUTHSCRIPT_DEFAULT_AUTH_TYPE = 0x01;
15
+ const AUTHSCRIPT_DOMAIN_SEPARATOR = 0x01;
16
+ const AUTHSCRIPT_DEFAULT_WITNESS_SCRIPT = Buffer.from([0x51]); // OP_TRUE
17
+ const AUTHSCRIPT_TAG = "NeuraiAuthScript";
18
+ const LEGACY_MESSAGE_PREFIX =
19
+ String.fromCharCode(Buffer.byteLength(MESSAGE_MAGIC, "utf8")) +
20
+ MESSAGE_MAGIC;
21
+
22
+ function encodeCompactSize(value: number): Buffer {
23
+ if (!Number.isInteger(value) || value < 0) {
24
+ throw new Error("CompactSize value must be a non-negative integer");
25
+ }
26
+
27
+ if (value < 253) {
28
+ return Buffer.from([value]);
29
+ }
30
+
31
+ if (value <= 0xffff) {
32
+ const buffer = Buffer.alloc(3);
33
+ buffer[0] = 0xfd;
34
+ buffer.writeUInt16LE(value, 1);
35
+ return buffer;
36
+ }
37
+
38
+ if (value <= 0xffffffff) {
39
+ const buffer = Buffer.alloc(5);
40
+ buffer[0] = 0xfe;
41
+ buffer.writeUInt32LE(value, 1);
42
+ return buffer;
43
+ }
44
+
45
+ throw new Error("CompactSize values above uint32 are not supported");
46
+ }
47
+
48
+ function decodeCompactSize(buffer: Buffer, offset: number) {
49
+ if (offset >= buffer.length) {
50
+ throw new Error("Unexpected end of CompactSize data");
51
+ }
52
+
53
+ const first = buffer[offset];
54
+ if (first < 253) {
55
+ return { value: first, offset: offset + 1 };
56
+ }
57
+
58
+ if (first === 0xfd) {
59
+ if (offset + 3 > buffer.length) {
60
+ throw new Error("Unexpected end of CompactSize uint16 data");
61
+ }
62
+ return { value: buffer.readUInt16LE(offset + 1), offset: offset + 3 };
63
+ }
64
+
65
+ if (first === 0xfe) {
66
+ if (offset + 5 > buffer.length) {
67
+ throw new Error("Unexpected end of CompactSize uint32 data");
68
+ }
69
+ return { value: buffer.readUInt32LE(offset + 1), offset: offset + 5 };
70
+ }
71
+
72
+ if (first === 0xff) {
73
+ throw new Error("CompactSize uint64 is not supported");
74
+ }
75
+
76
+ throw new Error("Invalid CompactSize prefix");
77
+ }
78
+
79
+ function sha256(bytes: Uint8Array) {
80
+ return createHash("sha256").update(bytes).digest();
81
+ }
82
+
83
+ function hash256(bytes: Uint8Array) {
84
+ return sha256(sha256(bytes));
85
+ }
86
+
87
+ function hash160(bytes: Uint8Array) {
88
+ return createHash("ripemd160").update(sha256(bytes)).digest();
89
+ }
90
+
91
+ function taggedHash(tag: string, bytes: Uint8Array) {
92
+ const tagHash = sha256(Buffer.from(tag, "utf8"));
93
+ return sha256(Buffer.concat([tagHash, tagHash, Buffer.from(bytes)]));
94
+ }
95
+
96
+ function encodeMessageHash(message: string) {
97
+ const messageBytes = Buffer.from(message, "utf8");
98
+ const magicBytes = Buffer.from(MESSAGE_MAGIC, "utf8");
99
+ const payload = Buffer.concat([
100
+ encodeCompactSize(magicBytes.length),
101
+ magicBytes,
102
+ encodeCompactSize(messageBytes.length),
103
+ messageBytes,
104
+ ]);
105
+
106
+ return hash256(payload);
107
+ }
108
+
109
+ function toSignatureBuffer(signature: string | Uint8Array) {
110
+ return typeof signature === "string"
111
+ ? Buffer.from(signature, "base64")
112
+ : Buffer.from(signature);
113
+ }
114
+
115
+ function normalizePQPublicKey(publicKey: Uint8Array) {
116
+ const buffer = Buffer.from(publicKey);
117
+
118
+ if (
119
+ buffer.length === PQ_SERIALIZED_PUBKEY_LENGTH &&
120
+ buffer[0] === PQ_SERIALIZED_PUBKEY_PREFIX
121
+ ) {
122
+ return buffer;
123
+ }
124
+
125
+ if (buffer.length === PQ_PUBLIC_KEY_LENGTH) {
126
+ return Buffer.concat([Buffer.from([PQ_SERIALIZED_PUBKEY_PREFIX]), buffer]);
127
+ }
128
+
129
+ throw new Error("Invalid PQ public key length");
130
+ }
131
+
132
+ function isPQMessageSignature(signature: string | Uint8Array) {
133
+ const buffer = toSignatureBuffer(signature);
134
+ return buffer.length > 0 && buffer[0] === PQ_MESSAGE_SIGNATURE_PREFIX;
135
+ }
136
+
137
+ function decodePQAddress(address: string) {
138
+ const decoded = bech32m.decode(address);
139
+ if (decoded.words.length === 0) {
140
+ throw new Error("Invalid bech32m address");
141
+ }
142
+
143
+ return {
144
+ prefix: decoded.prefix,
145
+ version: decoded.words[0],
146
+ program: Buffer.from(bech32m.fromWords(decoded.words.slice(1))),
147
+ };
148
+ }
149
+
150
+ function getDefaultPQAuthScriptCommitment(serializedPublicKey: Uint8Array) {
151
+ const authDescriptor = Buffer.concat([
152
+ Buffer.from([AUTHSCRIPT_DEFAULT_AUTH_TYPE]),
153
+ hash160(serializedPublicKey),
154
+ ]);
155
+ const witnessScriptHash = sha256(AUTHSCRIPT_DEFAULT_WITNESS_SCRIPT);
156
+ const preimage = Buffer.concat([
157
+ Buffer.from([AUTHSCRIPT_DOMAIN_SEPARATOR]),
158
+ authDescriptor,
159
+ witnessScriptHash,
160
+ ]);
161
+
162
+ return taggedHash(AUTHSCRIPT_TAG, preimage);
163
+ }
164
+
165
+ /** returns a base64 encoded string representation of the legacy signature */
166
+ export function sign(message: string, privateKey: Uint8Array, compressed = true) {
167
+ const signature = signLegacyMessage(
168
+ message,
169
+ Buffer.from(privateKey),
170
+ compressed,
171
+ LEGACY_MESSAGE_PREFIX
172
+ );
173
+
174
+ return signature.toString("base64");
175
+ }
176
+
177
+ export function signPQMessage(
178
+ message: string,
179
+ privateKey: Uint8Array,
180
+ publicKey: Uint8Array
181
+ ) {
182
+ const serializedPublicKey = normalizePQPublicKey(publicKey);
183
+ const hash = encodeMessageHash(message);
184
+ const pqSignature = Buffer.from(ml_dsa44.sign(hash, Buffer.from(privateKey)));
185
+
186
+ const payload = Buffer.concat([
187
+ Buffer.from([PQ_MESSAGE_SIGNATURE_PREFIX]),
188
+ encodeCompactSize(serializedPublicKey.length),
189
+ serializedPublicKey,
190
+ encodeCompactSize(pqSignature.length),
191
+ pqSignature,
192
+ ]);
193
+
194
+ return payload.toString("base64");
195
+ }
196
+
197
+ export function verifyLegacyMessage(
198
+ message: string,
199
+ address: string,
200
+ signature: string | Uint8Array
201
+ ) {
202
+ try {
203
+ return verifyLegacyCompactMessage(
204
+ message,
205
+ address,
206
+ toSignatureBuffer(signature),
207
+ LEGACY_MESSAGE_PREFIX
208
+ );
209
+ } catch {
210
+ return false;
211
+ }
212
+ }
213
+
214
+ export function verifyPQMessage(
215
+ message: string,
216
+ address: string,
217
+ signature: string | Uint8Array
218
+ ) {
219
+ try {
220
+ const payload = toSignatureBuffer(signature);
221
+ let offset = 0;
222
+
223
+ if (payload[offset++] !== PQ_MESSAGE_SIGNATURE_PREFIX) {
224
+ return false;
225
+ }
226
+
227
+ const publicKeyLength = decodeCompactSize(payload, offset);
228
+ offset = publicKeyLength.offset;
229
+
230
+ const serializedPublicKey = payload.subarray(
231
+ offset,
232
+ offset + publicKeyLength.value
233
+ );
234
+ offset += publicKeyLength.value;
235
+
236
+ const signatureLength = decodeCompactSize(payload, offset);
237
+ offset = signatureLength.offset;
238
+
239
+ const pqSignature = payload.subarray(offset, offset + signatureLength.value);
240
+ offset += signatureLength.value;
241
+
242
+ if (offset !== payload.length) {
243
+ return false;
244
+ }
245
+
246
+ if (
247
+ serializedPublicKey.length !== PQ_SERIALIZED_PUBKEY_LENGTH ||
248
+ serializedPublicKey[0] !== PQ_SERIALIZED_PUBKEY_PREFIX ||
249
+ pqSignature.length !== PQ_SIGNATURE_LENGTH
250
+ ) {
251
+ return false;
252
+ }
253
+
254
+ const decodedAddress = decodePQAddress(address);
255
+ if (
256
+ decodedAddress.version !== 1 ||
257
+ decodedAddress.program.length !== AUTHSCRIPT_PROGRAM_LENGTH
258
+ ) {
259
+ return false;
260
+ }
261
+
262
+ const expectedProgram = getDefaultPQAuthScriptCommitment(
263
+ serializedPublicKey
264
+ );
265
+ if (!expectedProgram.equals(decodedAddress.program)) {
266
+ return false;
267
+ }
268
+
269
+ return ml_dsa44.verify(
270
+ pqSignature,
271
+ encodeMessageHash(message),
272
+ serializedPublicKey.subarray(1)
273
+ );
274
+ } catch {
275
+ return false;
276
+ }
277
+ }
278
+
279
+ export function verifyMessage(
280
+ message: string,
281
+ address: string,
282
+ signature: string | Uint8Array
283
+ ) {
284
+ return isPQMessageSignature(signature)
285
+ ? verifyPQMessage(message, address, signature)
286
+ : verifyLegacyMessage(message, address, signature);
287
+ }
package/src/global.ts ADDED
@@ -0,0 +1,15 @@
1
+ import * as api from "./browser";
2
+
3
+ declare global {
4
+ interface Window {
5
+ NeuraiMessage: typeof api;
6
+ }
7
+ }
8
+
9
+ if (typeof globalThis !== "undefined") {
10
+ (globalThis as typeof globalThis & { NeuraiMessage: typeof api }).NeuraiMessage =
11
+ api;
12
+ }
13
+
14
+ export * from "./browser";
15
+ export default api;
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./core";