@theqrl/mldsa87 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -19,3 +19,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
22
+
package/README.md CHANGED
@@ -81,7 +81,7 @@ Generate a keypair from a seed.
81
81
 
82
82
  Sign a message (combined mode: returns signature || message).
83
83
 
84
- - `message`: `Uint8Array` - message to sign
84
+ - `message`: `Uint8Array` or `string` - message bytes; if `string`, it must be hex only (optional `0x`, even length). Plain-text strings are not accepted.
85
85
  - `sk`: `Uint8Array(4896)` - secret key
86
86
  - `randomized`: `boolean` - `true` for hedged signing, `false` for deterministic
87
87
  - `context`: `Uint8Array` (optional) - context string, 0-255 bytes. Default: `"ZOND"`
@@ -101,7 +101,7 @@ Verify and extract message from signed message.
101
101
  Create a detached signature.
102
102
 
103
103
  - `sig`: `Uint8Array(4627)` - output buffer for signature
104
- - `message`: `Uint8Array` - message to sign
104
+ - `message`: `Uint8Array` or `string` - message bytes; if `string`, it must be hex only (optional `0x`, even length). Plain-text strings are not accepted.
105
105
  - `sk`: `Uint8Array(4896)` - secret key
106
106
  - `randomized`: `boolean` - `true` for hedged, `false` for deterministic
107
107
  - `context`: `Uint8Array` (optional) - context string, 0-255 bytes
@@ -112,11 +112,13 @@ Create a detached signature.
112
112
  Verify a detached signature.
113
113
 
114
114
  - `sig`: `Uint8Array(4627)` - signature to verify
115
- - `message`: `Uint8Array` - original message
115
+ - `message`: `Uint8Array` or `string` - original message bytes; if `string`, it must be hex only (optional `0x`, even length). Plain-text strings are not accepted.
116
116
  - `pk`: `Uint8Array(2592)` - public key
117
117
  - `context`: `Uint8Array` (optional) - must match signing context
118
118
  - Returns: `true` if valid, `false` otherwise
119
119
 
120
+ **Note:** To sign or verify plain text, convert it to bytes (e.g., `new TextEncoder().encode('Hello')`). String inputs are interpreted as hex only.
121
+
120
122
  #### `zeroize(buffer)`
121
123
 
122
124
  Zero out sensitive data (best-effort, see security notes).
@@ -157,7 +159,7 @@ See [SECURITY.md](../../SECURITY.md) for important information about:
157
159
 
158
160
  ## Requirements
159
161
 
160
- - Node.js 18+ or modern browsers with ES2020 support
162
+ - Node.js 18.20+ or modern browsers with ES2020 support
161
163
  - Full TypeScript definitions included
162
164
 
163
165
  ## License
@@ -1,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
3
  var sha3_js = require('@noble/hashes/sha3.js');
4
- var pkg = require('randombytes');
5
4
  var utils_js = require('@noble/hashes/utils.js');
6
5
 
6
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
7
7
  const Shake128Rate = 168;
8
8
  const Shake256Rate = 136;
9
9
  const Stream128BlockBytes = Shake128Rate;
@@ -370,6 +370,8 @@ function polyUniform(a, seed, nonce) {
370
370
 
371
371
  let ctr = rejUniform(a.coeffs, 0, N, buf, bufLen);
372
372
 
373
+ // Note: With current parameters, needing extra blocks is vanishingly unlikely.
374
+ /* c8 ignore start */
373
375
  while (ctr < N) {
374
376
  off = bufLen % 3;
375
377
  for (let i = 0; i < off; ++i) buf[i] = buf[bufLen - off + i];
@@ -378,6 +380,7 @@ function polyUniform(a, seed, nonce) {
378
380
  bufLen = Stream128BlockBytes + off;
379
381
  ctr += rejUniform(a.coeffs, ctr, N - ctr, buf, bufLen);
380
382
  }
383
+ /* c8 ignore stop */
381
384
  }
382
385
 
383
386
  function rejEta(aP, aOffset, len, buf, bufLen) {
@@ -473,10 +476,13 @@ function polyChallenge(cP, seed) {
473
476
  }
474
477
  for (let i = N - TAU; i < N; ++i) {
475
478
  do {
479
+ // Note: Re-squeezing here is extremely unlikely with TAU=60.
480
+ /* c8 ignore start */
476
481
  if (pos >= Shake256Rate) {
477
482
  shake256SqueezeBlocks(buf, 0, 1, state);
478
483
  pos = 0;
479
484
  }
485
+ /* c8 ignore stop */
480
486
 
481
487
  b = buf[pos++];
482
488
  } while (b > i);
@@ -1021,7 +1027,69 @@ function unpackSig(cP, z, hP, sig) {
1021
1027
  return 0;
1022
1028
  }
1023
1029
 
1024
- const randomBytes = pkg;
1030
+ const MAX_BYTES = 65536;
1031
+
1032
+ function getGlobalScope() {
1033
+ if (typeof globalThis === 'object') return globalThis;
1034
+ if (typeof self === 'object') return self;
1035
+ if (typeof window === 'object') return window;
1036
+ if (typeof global === 'object') return global;
1037
+ return {};
1038
+ }
1039
+
1040
+ function getWebCrypto() {
1041
+ const scope = getGlobalScope();
1042
+ return scope.crypto || scope.msCrypto || null;
1043
+ }
1044
+
1045
+ function getNodeRandomBytes() {
1046
+ /* c8 ignore next */
1047
+ const isNode = typeof process === 'object' && process !== null && process.versions && process.versions.node;
1048
+ if (!isNode) return null;
1049
+
1050
+ const req =
1051
+ typeof module !== 'undefined' && module && typeof module.require === 'function'
1052
+ ? module.require.bind(module)
1053
+ : typeof module !== 'undefined' && module && typeof module.createRequire === 'function'
1054
+ ? module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('mldsa87.js', document.baseURI).href)))
1055
+ : typeof require === 'function'
1056
+ ? require
1057
+ : null;
1058
+ if (!req) return null;
1059
+
1060
+ try {
1061
+ const nodeCrypto = req('crypto');
1062
+ if (nodeCrypto && typeof nodeCrypto.randomBytes === 'function') {
1063
+ return nodeCrypto.randomBytes;
1064
+ }
1065
+ } catch {
1066
+ return null;
1067
+ }
1068
+
1069
+ return null;
1070
+ }
1071
+
1072
+ function randomBytes(size) {
1073
+ if (!Number.isSafeInteger(size) || size < 0) {
1074
+ throw new RangeError('size must be a non-negative integer');
1075
+ }
1076
+
1077
+ const cryptoObj = getWebCrypto();
1078
+ if (cryptoObj && typeof cryptoObj.getRandomValues === 'function') {
1079
+ const out = new Uint8Array(size);
1080
+ for (let i = 0; i < size; i += MAX_BYTES) {
1081
+ cryptoObj.getRandomValues(out.subarray(i, Math.min(size, i + MAX_BYTES)));
1082
+ }
1083
+ return out;
1084
+ }
1085
+
1086
+ const nodeRandomBytes = getNodeRandomBytes();
1087
+ if (nodeRandomBytes) {
1088
+ return nodeRandomBytes(size);
1089
+ }
1090
+
1091
+ throw new Error('Secure random number generation is not supported by this environment');
1092
+ }
1025
1093
 
1026
1094
  /**
1027
1095
  * Default signing context ("ZOND" in ASCII).
@@ -1032,15 +1100,27 @@ const DEFAULT_CTX = new Uint8Array([0x5a, 0x4f, 0x4e, 0x44]); // "ZOND"
1032
1100
 
1033
1101
  /**
1034
1102
  * Convert hex string to Uint8Array with strict validation.
1103
+ *
1104
+ * NOTE: This function accepts multiple hex formats (with/without 0x prefix,
1105
+ * leading/trailing whitespace). While user-friendly, this flexibility could
1106
+ * mask input errors. Applications requiring strict format validation should
1107
+ * validate hex format before calling cryptographic functions, e.g.:
1108
+ * - Reject strings with 0x prefix if raw hex is expected
1109
+ * - Reject strings with whitespace
1110
+ * - Enforce consistent casing (lowercase/uppercase)
1111
+ *
1035
1112
  * @param {string} hex - Hex string (optional 0x prefix, even length).
1036
1113
  * @returns {Uint8Array} Decoded bytes.
1037
1114
  * @private
1038
1115
  */
1039
1116
  function hexToBytes(hex) {
1117
+ /* c8 ignore start */
1040
1118
  if (typeof hex !== 'string') {
1041
1119
  throw new Error('message must be a hex string');
1042
1120
  }
1121
+ /* c8 ignore stop */
1043
1122
  let clean = hex.trim();
1123
+ // Accepts both "0x..." and raw hex formats for convenience
1044
1124
  if (clean.startsWith('0x') || clean.startsWith('0X')) {
1045
1125
  clean = clean.slice(2);
1046
1126
  }
@@ -1060,7 +1140,7 @@ function messageToBytes(message) {
1060
1140
  if (message instanceof Uint8Array) {
1061
1141
  return message;
1062
1142
  }
1063
- return null;
1143
+ throw new Error('message must be Uint8Array or hex string');
1064
1144
  }
1065
1145
 
1066
1146
  /**
@@ -1208,9 +1288,6 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning, ctx = DEFAULT_CTX) {
1208
1288
  pre.set(ctx, 2);
1209
1289
 
1210
1290
  const mBytes = messageToBytes(m);
1211
- if (!mBytes) {
1212
- throw new Error('message must be Uint8Array or hex string');
1213
- }
1214
1291
 
1215
1292
  // mu = SHAKE256(tr || pre || m)
1216
1293
  const mu = sha3_js.shake256.create({}).update(tr).update(pre).update(mBytes).xof(CRHBytes);
@@ -1268,15 +1345,19 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning, ctx = DEFAULT_CTX) {
1268
1345
  polyVecKPointWisePolyMontgomery(h, cp, t0);
1269
1346
  polyVecKInvNTTToMont(h);
1270
1347
  polyVecKReduce(h);
1348
+ /* c8 ignore start */
1271
1349
  if (polyVecKChkNorm(h, GAMMA2) !== 0) {
1272
1350
  continue;
1273
1351
  }
1352
+ /* c8 ignore stop */
1274
1353
 
1275
1354
  polyVecKAdd(w0, w0, h);
1276
1355
  const n = polyVecKMakeHint(h, w0, w1);
1356
+ /* c8 ignore start */
1277
1357
  if (n > OMEGA) {
1278
1358
  continue;
1279
1359
  }
1360
+ /* c8 ignore stop */
1280
1361
 
1281
1362
  packSig(sig, ctilde, z, h);
1282
1363
  return 0;
@@ -1303,9 +1384,6 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning, ctx = DEFAULT_CTX) {
1303
1384
  */
1304
1385
  function cryptoSign(msg, sk, randomizedSigning, ctx = DEFAULT_CTX) {
1305
1386
  const msgBytes = messageToBytes(msg);
1306
- if (!msgBytes) {
1307
- throw new Error('message must be Uint8Array or hex string');
1308
- }
1309
1387
 
1310
1388
  const sm = new Uint8Array(CryptoBytes + msgBytes.length);
1311
1389
  const mLen = msgBytes.length;
@@ -1314,9 +1392,11 @@ function cryptoSign(msg, sk, randomizedSigning, ctx = DEFAULT_CTX) {
1314
1392
  }
1315
1393
  const result = cryptoSignSignature(sm, msgBytes, sk, randomizedSigning, ctx);
1316
1394
 
1395
+ /* c8 ignore start */
1317
1396
  if (result !== 0) {
1318
1397
  throw new Error('failed to sign');
1319
1398
  }
1399
+ /* c8 ignore stop */
1320
1400
  return sm;
1321
1401
  }
1322
1402
 
@@ -1383,9 +1463,6 @@ function cryptoSignVerify(sig, m, pk, ctx = DEFAULT_CTX) {
1383
1463
  } catch {
1384
1464
  return false;
1385
1465
  }
1386
- if (!mBytes) {
1387
- return false;
1388
- }
1389
1466
  const muFull = sha3_js.shake256.create({}).update(tr).update(pre).update(mBytes).xof(CRHBytes);
1390
1467
  mu.set(muFull);
1391
1468
 
@@ -1,5 +1,4 @@
1
1
  import { shake128, shake256 } from '@noble/hashes/sha3.js';
2
- import pkg from 'randombytes';
3
2
  import { hexToBytes as hexToBytes$1 } from '@noble/hashes/utils.js';
4
3
 
5
4
  const Shake128Rate = 168;
@@ -368,6 +367,8 @@ function polyUniform(a, seed, nonce) {
368
367
 
369
368
  let ctr = rejUniform(a.coeffs, 0, N, buf, bufLen);
370
369
 
370
+ // Note: With current parameters, needing extra blocks is vanishingly unlikely.
371
+ /* c8 ignore start */
371
372
  while (ctr < N) {
372
373
  off = bufLen % 3;
373
374
  for (let i = 0; i < off; ++i) buf[i] = buf[bufLen - off + i];
@@ -376,6 +377,7 @@ function polyUniform(a, seed, nonce) {
376
377
  bufLen = Stream128BlockBytes + off;
377
378
  ctr += rejUniform(a.coeffs, ctr, N - ctr, buf, bufLen);
378
379
  }
380
+ /* c8 ignore stop */
379
381
  }
380
382
 
381
383
  function rejEta(aP, aOffset, len, buf, bufLen) {
@@ -471,10 +473,13 @@ function polyChallenge(cP, seed) {
471
473
  }
472
474
  for (let i = N - TAU; i < N; ++i) {
473
475
  do {
476
+ // Note: Re-squeezing here is extremely unlikely with TAU=60.
477
+ /* c8 ignore start */
474
478
  if (pos >= Shake256Rate) {
475
479
  shake256SqueezeBlocks(buf, 0, 1, state);
476
480
  pos = 0;
477
481
  }
482
+ /* c8 ignore stop */
478
483
 
479
484
  b = buf[pos++];
480
485
  } while (b > i);
@@ -1019,7 +1024,69 @@ function unpackSig(cP, z, hP, sig) {
1019
1024
  return 0;
1020
1025
  }
1021
1026
 
1022
- const randomBytes = pkg;
1027
+ const MAX_BYTES = 65536;
1028
+
1029
+ function getGlobalScope() {
1030
+ if (typeof globalThis === 'object') return globalThis;
1031
+ if (typeof self === 'object') return self;
1032
+ if (typeof window === 'object') return window;
1033
+ if (typeof global === 'object') return global;
1034
+ return {};
1035
+ }
1036
+
1037
+ function getWebCrypto() {
1038
+ const scope = getGlobalScope();
1039
+ return scope.crypto || scope.msCrypto || null;
1040
+ }
1041
+
1042
+ function getNodeRandomBytes() {
1043
+ /* c8 ignore next */
1044
+ const isNode = typeof process === 'object' && process !== null && process.versions && process.versions.node;
1045
+ if (!isNode) return null;
1046
+
1047
+ const req =
1048
+ typeof module !== 'undefined' && module && typeof module.require === 'function'
1049
+ ? module.require.bind(module)
1050
+ : typeof module !== 'undefined' && module && typeof module.createRequire === 'function'
1051
+ ? module.createRequire(import.meta.url)
1052
+ : typeof require === 'function'
1053
+ ? require
1054
+ : null;
1055
+ if (!req) return null;
1056
+
1057
+ try {
1058
+ const nodeCrypto = req('crypto');
1059
+ if (nodeCrypto && typeof nodeCrypto.randomBytes === 'function') {
1060
+ return nodeCrypto.randomBytes;
1061
+ }
1062
+ } catch {
1063
+ return null;
1064
+ }
1065
+
1066
+ return null;
1067
+ }
1068
+
1069
+ function randomBytes(size) {
1070
+ if (!Number.isSafeInteger(size) || size < 0) {
1071
+ throw new RangeError('size must be a non-negative integer');
1072
+ }
1073
+
1074
+ const cryptoObj = getWebCrypto();
1075
+ if (cryptoObj && typeof cryptoObj.getRandomValues === 'function') {
1076
+ const out = new Uint8Array(size);
1077
+ for (let i = 0; i < size; i += MAX_BYTES) {
1078
+ cryptoObj.getRandomValues(out.subarray(i, Math.min(size, i + MAX_BYTES)));
1079
+ }
1080
+ return out;
1081
+ }
1082
+
1083
+ const nodeRandomBytes = getNodeRandomBytes();
1084
+ if (nodeRandomBytes) {
1085
+ return nodeRandomBytes(size);
1086
+ }
1087
+
1088
+ throw new Error('Secure random number generation is not supported by this environment');
1089
+ }
1023
1090
 
1024
1091
  /**
1025
1092
  * Default signing context ("ZOND" in ASCII).
@@ -1030,15 +1097,27 @@ const DEFAULT_CTX = new Uint8Array([0x5a, 0x4f, 0x4e, 0x44]); // "ZOND"
1030
1097
 
1031
1098
  /**
1032
1099
  * Convert hex string to Uint8Array with strict validation.
1100
+ *
1101
+ * NOTE: This function accepts multiple hex formats (with/without 0x prefix,
1102
+ * leading/trailing whitespace). While user-friendly, this flexibility could
1103
+ * mask input errors. Applications requiring strict format validation should
1104
+ * validate hex format before calling cryptographic functions, e.g.:
1105
+ * - Reject strings with 0x prefix if raw hex is expected
1106
+ * - Reject strings with whitespace
1107
+ * - Enforce consistent casing (lowercase/uppercase)
1108
+ *
1033
1109
  * @param {string} hex - Hex string (optional 0x prefix, even length).
1034
1110
  * @returns {Uint8Array} Decoded bytes.
1035
1111
  * @private
1036
1112
  */
1037
1113
  function hexToBytes(hex) {
1114
+ /* c8 ignore start */
1038
1115
  if (typeof hex !== 'string') {
1039
1116
  throw new Error('message must be a hex string');
1040
1117
  }
1118
+ /* c8 ignore stop */
1041
1119
  let clean = hex.trim();
1120
+ // Accepts both "0x..." and raw hex formats for convenience
1042
1121
  if (clean.startsWith('0x') || clean.startsWith('0X')) {
1043
1122
  clean = clean.slice(2);
1044
1123
  }
@@ -1058,7 +1137,7 @@ function messageToBytes(message) {
1058
1137
  if (message instanceof Uint8Array) {
1059
1138
  return message;
1060
1139
  }
1061
- return null;
1140
+ throw new Error('message must be Uint8Array or hex string');
1062
1141
  }
1063
1142
 
1064
1143
  /**
@@ -1206,9 +1285,6 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning, ctx = DEFAULT_CTX) {
1206
1285
  pre.set(ctx, 2);
1207
1286
 
1208
1287
  const mBytes = messageToBytes(m);
1209
- if (!mBytes) {
1210
- throw new Error('message must be Uint8Array or hex string');
1211
- }
1212
1288
 
1213
1289
  // mu = SHAKE256(tr || pre || m)
1214
1290
  const mu = shake256.create({}).update(tr).update(pre).update(mBytes).xof(CRHBytes);
@@ -1266,15 +1342,19 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning, ctx = DEFAULT_CTX) {
1266
1342
  polyVecKPointWisePolyMontgomery(h, cp, t0);
1267
1343
  polyVecKInvNTTToMont(h);
1268
1344
  polyVecKReduce(h);
1345
+ /* c8 ignore start */
1269
1346
  if (polyVecKChkNorm(h, GAMMA2) !== 0) {
1270
1347
  continue;
1271
1348
  }
1349
+ /* c8 ignore stop */
1272
1350
 
1273
1351
  polyVecKAdd(w0, w0, h);
1274
1352
  const n = polyVecKMakeHint(h, w0, w1);
1353
+ /* c8 ignore start */
1275
1354
  if (n > OMEGA) {
1276
1355
  continue;
1277
1356
  }
1357
+ /* c8 ignore stop */
1278
1358
 
1279
1359
  packSig(sig, ctilde, z, h);
1280
1360
  return 0;
@@ -1301,9 +1381,6 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning, ctx = DEFAULT_CTX) {
1301
1381
  */
1302
1382
  function cryptoSign(msg, sk, randomizedSigning, ctx = DEFAULT_CTX) {
1303
1383
  const msgBytes = messageToBytes(msg);
1304
- if (!msgBytes) {
1305
- throw new Error('message must be Uint8Array or hex string');
1306
- }
1307
1384
 
1308
1385
  const sm = new Uint8Array(CryptoBytes + msgBytes.length);
1309
1386
  const mLen = msgBytes.length;
@@ -1312,9 +1389,11 @@ function cryptoSign(msg, sk, randomizedSigning, ctx = DEFAULT_CTX) {
1312
1389
  }
1313
1390
  const result = cryptoSignSignature(sm, msgBytes, sk, randomizedSigning, ctx);
1314
1391
 
1392
+ /* c8 ignore start */
1315
1393
  if (result !== 0) {
1316
1394
  throw new Error('failed to sign');
1317
1395
  }
1396
+ /* c8 ignore stop */
1318
1397
  return sm;
1319
1398
  }
1320
1399
 
@@ -1381,9 +1460,6 @@ function cryptoSignVerify(sig, m, pk, ctx = DEFAULT_CTX) {
1381
1460
  } catch {
1382
1461
  return false;
1383
1462
  }
1384
- if (!mBytes) {
1385
- return false;
1386
- }
1387
1463
  const muFull = shake256.create({}).update(tr).update(pre).update(mBytes).xof(CRHBytes);
1388
1464
  mu.set(muFull);
1389
1465
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theqrl/mldsa87",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "ML-DSA-87 cryptography",
5
5
  "keywords": [
6
6
  "ml-dsa",
@@ -31,10 +31,12 @@
31
31
  "url": "git+https://github.com/theQRL/qrypto.js.git"
32
32
  },
33
33
  "scripts": {
34
- "test": "../../node_modules/mocha/bin/mocha.js --timeout 10000",
34
+ "test": "../../node_modules/mocha/bin/mocha.js --require ../../scripts/node-test-setup.cjs --timeout 10000",
35
+ "test:browser": "playwright test",
35
36
  "build": "rollup src/index.js --file ./dist/cjs/mldsa87.js --format cjs && rollup src/index.js --file ./dist/mjs/mldsa87.js --format esm && ./fixup",
36
37
  "lint-check": "eslint 'src/**/*.js' 'test/**/*.js'",
37
38
  "lint": "eslint --fix 'src/**/*.js' 'test/**/*.js'",
39
+ "coverage": "c8 npm run test",
38
40
  "report-coverage": "c8 --reporter=text-lcov npm run test > coverage.lcov"
39
41
  },
40
42
  "bugs": {
@@ -47,6 +49,9 @@
47
49
  }
48
50
  },
49
51
  "type": "module",
52
+ "engines": {
53
+ "node": ">=18.20.0"
54
+ },
50
55
  "devDependencies": {
51
56
  "@eslint/js": "^9.39.0",
52
57
  "c8": "^10.1.3",
@@ -61,7 +66,6 @@
61
66
  "rollup": "^4.55.1"
62
67
  },
63
68
  "dependencies": {
64
- "@noble/hashes": "^2.0.1",
65
- "randombytes": "^2.1.0"
69
+ "@noble/hashes": "^2.0.1"
66
70
  }
67
71
  }
package/src/index.d.ts CHANGED
@@ -58,7 +58,7 @@ export function cryptoSignKeypair(
58
58
  /**
59
59
  * Create a signature for a message with optional context
60
60
  * @param sig - Output buffer for signature (must be CryptoBytes length minimum)
61
- * @param m - Message to sign (hex string or Uint8Array)
61
+ * @param m - Message to sign (hex string or Uint8Array; strings are parsed as hex only)
62
62
  * @param sk - Secret key
63
63
  * @param randomizedSigning - If true, use random nonce; if false, deterministic
64
64
  * @param ctx - Optional context string (max 255 bytes, defaults to "ZOND")
@@ -92,7 +92,7 @@ export function cryptoSign(
92
92
  /**
93
93
  * Verify a signature with optional context
94
94
  * @param sig - Signature to verify
95
- * @param m - Message that was signed (hex string or Uint8Array)
95
+ * @param m - Message that was signed (hex string or Uint8Array; strings are parsed as hex only)
96
96
  * @param pk - Public key
97
97
  * @param ctx - Optional context string (max 255 bytes, defaults to "ZOND")
98
98
  * @returns true if signature is valid, false otherwise