@theqrl/dilithium5 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
@@ -64,7 +64,7 @@ Generate a keypair from a seed.
64
64
 
65
65
  Sign a message (combined mode: returns signature || message).
66
66
 
67
- - `message`: `Uint8Array` - message to sign
67
+ - `message`: `Uint8Array` or `string` - message bytes; if `string`, it must be hex only (optional `0x`, even length). Plain-text strings are not accepted.
68
68
  - `sk`: `Uint8Array(4896)` - secret key
69
69
  - `randomized`: `boolean` - `true` for hedged signing, `false` for deterministic
70
70
  - Returns: `Uint8Array` containing signature + message
@@ -82,7 +82,7 @@ Verify and extract message from signed message.
82
82
  Create a detached signature.
83
83
 
84
84
  - `sig`: `Uint8Array(4595)` - output buffer for signature
85
- - `message`: `Uint8Array` - message to sign
85
+ - `message`: `Uint8Array` or `string` - message bytes; if `string`, it must be hex only (optional `0x`, even length). Plain-text strings are not accepted.
86
86
  - `sk`: `Uint8Array(4896)` - secret key
87
87
  - `randomized`: `boolean` - `true` for hedged, `false` for deterministic
88
88
  - Returns: `0` on success
@@ -92,10 +92,12 @@ Create a detached signature.
92
92
  Verify a detached signature.
93
93
 
94
94
  - `sig`: `Uint8Array(4595)` - signature to verify
95
- - `message`: `Uint8Array` - original message
95
+ - `message`: `Uint8Array` or `string` - original message bytes; if `string`, it must be hex only (optional `0x`, even length). Plain-text strings are not accepted.
96
96
  - `pk`: `Uint8Array(2592)` - public key
97
97
  - Returns: `true` if valid, `false` otherwise
98
98
 
99
+ **Note:** To sign or verify plain text, convert it to bytes (e.g., `new TextEncoder().encode('Hello')`). String inputs are interpreted as hex only.
100
+
99
101
  #### `zeroize(buffer)`
100
102
 
101
103
  Zero out sensitive data (best-effort, see security notes).
@@ -139,7 +141,7 @@ See [SECURITY.md](../../SECURITY.md) for important information about:
139
141
 
140
142
  ## Requirements
141
143
 
142
- - Node.js 18+ or modern browsers with ES2020 support
144
+ - Node.js 18.20+ or modern browsers with ES2020 support
143
145
  - Full TypeScript definitions included
144
146
 
145
147
  ## 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;
@@ -368,6 +368,8 @@ function polyUniform(a, seed, nonce) {
368
368
 
369
369
  let ctr = rejUniform(a.coeffs, 0, N, buf, bufLen);
370
370
 
371
+ // Note: With current parameters, needing extra blocks is vanishingly unlikely.
372
+ /* c8 ignore start */
371
373
  while (ctr < N) {
372
374
  off = bufLen % 3;
373
375
  for (let i = 0; i < off; ++i) buf[i] = buf[bufLen - off + i];
@@ -376,6 +378,7 @@ function polyUniform(a, seed, nonce) {
376
378
  bufLen = Stream128BlockBytes + off;
377
379
  ctr += rejUniform(a.coeffs, ctr, N - ctr, buf, bufLen);
378
380
  }
381
+ /* c8 ignore stop */
379
382
  }
380
383
 
381
384
  function rejEta(aP, aOffset, len, buf, bufLen) {
@@ -469,10 +472,13 @@ function polyChallenge(cP, seed) {
469
472
  }
470
473
  for (let i = N - TAU; i < N; ++i) {
471
474
  do {
475
+ // Note: Re-squeezing here is extremely unlikely with TAU=60.
476
+ /* c8 ignore start */
472
477
  if (pos >= Shake256Rate) {
473
478
  shake256SqueezeBlocks(buf, 0, 1, state);
474
479
  pos = 0;
475
480
  }
481
+ /* c8 ignore stop */
476
482
 
477
483
  b = buf[pos++];
478
484
  } while (b > i);
@@ -1017,19 +1023,98 @@ function unpackSig(cP, z, hP, sig) {
1017
1023
  return 0;
1018
1024
  }
1019
1025
 
1020
- const randomBytes = pkg;
1026
+ const MAX_BYTES = 65536;
1027
+ const MAX_UINT32 = 0xffffffff;
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((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('dilithium5.js', document.baseURI).href)))
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
+ if (size > MAX_UINT32) {
1074
+ throw new RangeError('requested too many random bytes');
1075
+ }
1076
+ if (size === 0) return new Uint8Array(0);
1077
+
1078
+ const cryptoObj = getWebCrypto();
1079
+ if (cryptoObj && typeof cryptoObj.getRandomValues === 'function') {
1080
+ const out = new Uint8Array(size);
1081
+ for (let i = 0; i < size; i += MAX_BYTES) {
1082
+ cryptoObj.getRandomValues(out.subarray(i, Math.min(size, i + MAX_BYTES)));
1083
+ }
1084
+ return out;
1085
+ }
1086
+
1087
+ const nodeRandomBytes = getNodeRandomBytes();
1088
+ if (nodeRandomBytes) {
1089
+ return nodeRandomBytes(size);
1090
+ }
1091
+
1092
+ throw new Error('Secure random number generation is not supported by this environment');
1093
+ }
1021
1094
 
1022
1095
  /**
1023
1096
  * Convert hex string to Uint8Array with strict validation.
1097
+ *
1098
+ * NOTE: This function accepts multiple hex formats (with/without 0x prefix,
1099
+ * leading/trailing whitespace). While user-friendly, this flexibility could
1100
+ * mask input errors. Applications requiring strict format validation should
1101
+ * validate hex format before calling cryptographic functions, e.g.:
1102
+ * - Reject strings with 0x prefix if raw hex is expected
1103
+ * - Reject strings with whitespace
1104
+ * - Enforce consistent casing (lowercase/uppercase)
1105
+ *
1024
1106
  * @param {string} hex - Hex string (optional 0x prefix, even length).
1025
1107
  * @returns {Uint8Array} Decoded bytes.
1026
1108
  * @private
1027
1109
  */
1028
1110
  function hexToBytes(hex) {
1111
+ /* c8 ignore start */
1029
1112
  if (typeof hex !== 'string') {
1030
1113
  throw new Error('message must be a hex string');
1031
1114
  }
1115
+ /* c8 ignore stop */
1032
1116
  let clean = hex.trim();
1117
+ // Accepts both "0x..." and raw hex formats for convenience
1033
1118
  if (clean.startsWith('0x') || clean.startsWith('0X')) {
1034
1119
  clean = clean.slice(2);
1035
1120
  }
@@ -1049,7 +1134,7 @@ function messageToBytes(message) {
1049
1134
  if (message instanceof Uint8Array) {
1050
1135
  return message;
1051
1136
  }
1052
- return null;
1137
+ throw new Error('message must be Uint8Array or hex string');
1053
1138
  }
1054
1139
 
1055
1140
  /**
@@ -1161,9 +1246,6 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
1161
1246
  }
1162
1247
 
1163
1248
  const mBytes = messageToBytes(m);
1164
- if (!mBytes) {
1165
- throw new Error('message must be Uint8Array or hex string');
1166
- }
1167
1249
 
1168
1250
  const rho = new Uint8Array(SeedBytes);
1169
1251
  const tr = new Uint8Array(TRBytes);
@@ -1242,15 +1324,19 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
1242
1324
  polyVecKPointWisePolyMontgomery(h, cp, t0);
1243
1325
  polyVecKInvNTTToMont(h);
1244
1326
  polyVecKReduce(h);
1327
+ /* c8 ignore start */
1245
1328
  if (polyVecKChkNorm(h, GAMMA2) !== 0) {
1246
1329
  continue;
1247
1330
  }
1331
+ /* c8 ignore stop */
1248
1332
 
1249
1333
  polyVecKAdd(w0, w0, h);
1250
1334
  const n = polyVecKMakeHint(h, w0, w1);
1335
+ /* c8 ignore start */
1251
1336
  if (n > OMEGA) {
1252
1337
  continue;
1253
1338
  }
1339
+ /* c8 ignore stop */
1254
1340
 
1255
1341
  packSig(sig, sig, z, h);
1256
1342
  return 0;
@@ -1275,9 +1361,6 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
1275
1361
  */
1276
1362
  function cryptoSign(msg, sk, randomizedSigning) {
1277
1363
  const msgBytes = messageToBytes(msg);
1278
- if (!msgBytes) {
1279
- throw new Error('message must be Uint8Array or hex string');
1280
- }
1281
1364
 
1282
1365
  const sm = new Uint8Array(CryptoBytes + msgBytes.length);
1283
1366
  const mLen = msgBytes.length;
@@ -1286,9 +1369,11 @@ function cryptoSign(msg, sk, randomizedSigning) {
1286
1369
  }
1287
1370
  const result = cryptoSignSignature(sm, msgBytes, sk, randomizedSigning);
1288
1371
 
1372
+ /* c8 ignore start */
1289
1373
  if (result !== 0) {
1290
1374
  throw new Error('failed to sign');
1291
1375
  }
1376
+ /* c8 ignore stop */
1292
1377
  return sm;
1293
1378
  }
1294
1379
 
@@ -1347,9 +1432,6 @@ function cryptoSignVerify(sig, m, pk) {
1347
1432
  } catch {
1348
1433
  return false;
1349
1434
  }
1350
- if (!mBytes) {
1351
- return false;
1352
- }
1353
1435
  const muFull = sha3_js.shake256.create({}).update(mu.slice(0, TRBytes)).update(mBytes).xof(CRHBytes);
1354
1436
  mu.set(muFull);
1355
1437
 
@@ -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;
@@ -366,6 +365,8 @@ function polyUniform(a, seed, nonce) {
366
365
 
367
366
  let ctr = rejUniform(a.coeffs, 0, N, buf, bufLen);
368
367
 
368
+ // Note: With current parameters, needing extra blocks is vanishingly unlikely.
369
+ /* c8 ignore start */
369
370
  while (ctr < N) {
370
371
  off = bufLen % 3;
371
372
  for (let i = 0; i < off; ++i) buf[i] = buf[bufLen - off + i];
@@ -374,6 +375,7 @@ function polyUniform(a, seed, nonce) {
374
375
  bufLen = Stream128BlockBytes + off;
375
376
  ctr += rejUniform(a.coeffs, ctr, N - ctr, buf, bufLen);
376
377
  }
378
+ /* c8 ignore stop */
377
379
  }
378
380
 
379
381
  function rejEta(aP, aOffset, len, buf, bufLen) {
@@ -467,10 +469,13 @@ function polyChallenge(cP, seed) {
467
469
  }
468
470
  for (let i = N - TAU; i < N; ++i) {
469
471
  do {
472
+ // Note: Re-squeezing here is extremely unlikely with TAU=60.
473
+ /* c8 ignore start */
470
474
  if (pos >= Shake256Rate) {
471
475
  shake256SqueezeBlocks(buf, 0, 1, state);
472
476
  pos = 0;
473
477
  }
478
+ /* c8 ignore stop */
474
479
 
475
480
  b = buf[pos++];
476
481
  } while (b > i);
@@ -1015,19 +1020,98 @@ function unpackSig(cP, z, hP, sig) {
1015
1020
  return 0;
1016
1021
  }
1017
1022
 
1018
- const randomBytes = pkg;
1023
+ const MAX_BYTES = 65536;
1024
+ const MAX_UINT32 = 0xffffffff;
1025
+
1026
+ function getGlobalScope() {
1027
+ if (typeof globalThis === 'object') return globalThis;
1028
+ if (typeof self === 'object') return self;
1029
+ if (typeof window === 'object') return window;
1030
+ if (typeof global === 'object') return global;
1031
+ return {};
1032
+ }
1033
+
1034
+ function getWebCrypto() {
1035
+ const scope = getGlobalScope();
1036
+ return scope.crypto || scope.msCrypto || null;
1037
+ }
1038
+
1039
+ function getNodeRandomBytes() {
1040
+ /* c8 ignore next */
1041
+ const isNode = typeof process === 'object' && process !== null && process.versions && process.versions.node;
1042
+ if (!isNode) return null;
1043
+
1044
+ const req =
1045
+ typeof module !== 'undefined' && module && typeof module.require === 'function'
1046
+ ? module.require.bind(module)
1047
+ : typeof module !== 'undefined' && module && typeof module.createRequire === 'function'
1048
+ ? module.createRequire(import.meta.url)
1049
+ : typeof require === 'function'
1050
+ ? require
1051
+ : null;
1052
+ if (!req) return null;
1053
+
1054
+ try {
1055
+ const nodeCrypto = req('crypto');
1056
+ if (nodeCrypto && typeof nodeCrypto.randomBytes === 'function') {
1057
+ return nodeCrypto.randomBytes;
1058
+ }
1059
+ } catch {
1060
+ return null;
1061
+ }
1062
+
1063
+ return null;
1064
+ }
1065
+
1066
+ function randomBytes(size) {
1067
+ if (!Number.isSafeInteger(size) || size < 0) {
1068
+ throw new RangeError('size must be a non-negative integer');
1069
+ }
1070
+ if (size > MAX_UINT32) {
1071
+ throw new RangeError('requested too many random bytes');
1072
+ }
1073
+ if (size === 0) return new Uint8Array(0);
1074
+
1075
+ const cryptoObj = getWebCrypto();
1076
+ if (cryptoObj && typeof cryptoObj.getRandomValues === 'function') {
1077
+ const out = new Uint8Array(size);
1078
+ for (let i = 0; i < size; i += MAX_BYTES) {
1079
+ cryptoObj.getRandomValues(out.subarray(i, Math.min(size, i + MAX_BYTES)));
1080
+ }
1081
+ return out;
1082
+ }
1083
+
1084
+ const nodeRandomBytes = getNodeRandomBytes();
1085
+ if (nodeRandomBytes) {
1086
+ return nodeRandomBytes(size);
1087
+ }
1088
+
1089
+ throw new Error('Secure random number generation is not supported by this environment');
1090
+ }
1019
1091
 
1020
1092
  /**
1021
1093
  * Convert hex string to Uint8Array with strict validation.
1094
+ *
1095
+ * NOTE: This function accepts multiple hex formats (with/without 0x prefix,
1096
+ * leading/trailing whitespace). While user-friendly, this flexibility could
1097
+ * mask input errors. Applications requiring strict format validation should
1098
+ * validate hex format before calling cryptographic functions, e.g.:
1099
+ * - Reject strings with 0x prefix if raw hex is expected
1100
+ * - Reject strings with whitespace
1101
+ * - Enforce consistent casing (lowercase/uppercase)
1102
+ *
1022
1103
  * @param {string} hex - Hex string (optional 0x prefix, even length).
1023
1104
  * @returns {Uint8Array} Decoded bytes.
1024
1105
  * @private
1025
1106
  */
1026
1107
  function hexToBytes(hex) {
1108
+ /* c8 ignore start */
1027
1109
  if (typeof hex !== 'string') {
1028
1110
  throw new Error('message must be a hex string');
1029
1111
  }
1112
+ /* c8 ignore stop */
1030
1113
  let clean = hex.trim();
1114
+ // Accepts both "0x..." and raw hex formats for convenience
1031
1115
  if (clean.startsWith('0x') || clean.startsWith('0X')) {
1032
1116
  clean = clean.slice(2);
1033
1117
  }
@@ -1047,7 +1131,7 @@ function messageToBytes(message) {
1047
1131
  if (message instanceof Uint8Array) {
1048
1132
  return message;
1049
1133
  }
1050
- return null;
1134
+ throw new Error('message must be Uint8Array or hex string');
1051
1135
  }
1052
1136
 
1053
1137
  /**
@@ -1159,9 +1243,6 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
1159
1243
  }
1160
1244
 
1161
1245
  const mBytes = messageToBytes(m);
1162
- if (!mBytes) {
1163
- throw new Error('message must be Uint8Array or hex string');
1164
- }
1165
1246
 
1166
1247
  const rho = new Uint8Array(SeedBytes);
1167
1248
  const tr = new Uint8Array(TRBytes);
@@ -1240,15 +1321,19 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
1240
1321
  polyVecKPointWisePolyMontgomery(h, cp, t0);
1241
1322
  polyVecKInvNTTToMont(h);
1242
1323
  polyVecKReduce(h);
1324
+ /* c8 ignore start */
1243
1325
  if (polyVecKChkNorm(h, GAMMA2) !== 0) {
1244
1326
  continue;
1245
1327
  }
1328
+ /* c8 ignore stop */
1246
1329
 
1247
1330
  polyVecKAdd(w0, w0, h);
1248
1331
  const n = polyVecKMakeHint(h, w0, w1);
1332
+ /* c8 ignore start */
1249
1333
  if (n > OMEGA) {
1250
1334
  continue;
1251
1335
  }
1336
+ /* c8 ignore stop */
1252
1337
 
1253
1338
  packSig(sig, sig, z, h);
1254
1339
  return 0;
@@ -1273,9 +1358,6 @@ function cryptoSignSignature(sig, m, sk, randomizedSigning) {
1273
1358
  */
1274
1359
  function cryptoSign(msg, sk, randomizedSigning) {
1275
1360
  const msgBytes = messageToBytes(msg);
1276
- if (!msgBytes) {
1277
- throw new Error('message must be Uint8Array or hex string');
1278
- }
1279
1361
 
1280
1362
  const sm = new Uint8Array(CryptoBytes + msgBytes.length);
1281
1363
  const mLen = msgBytes.length;
@@ -1284,9 +1366,11 @@ function cryptoSign(msg, sk, randomizedSigning) {
1284
1366
  }
1285
1367
  const result = cryptoSignSignature(sm, msgBytes, sk, randomizedSigning);
1286
1368
 
1369
+ /* c8 ignore start */
1287
1370
  if (result !== 0) {
1288
1371
  throw new Error('failed to sign');
1289
1372
  }
1373
+ /* c8 ignore stop */
1290
1374
  return sm;
1291
1375
  }
1292
1376
 
@@ -1345,9 +1429,6 @@ function cryptoSignVerify(sig, m, pk) {
1345
1429
  } catch {
1346
1430
  return false;
1347
1431
  }
1348
- if (!mBytes) {
1349
- return false;
1350
- }
1351
1432
  const muFull = shake256.create({}).update(mu.slice(0, TRBytes)).update(mBytes).xof(CRHBytes);
1352
1433
  mu.set(muFull);
1353
1434
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theqrl/dilithium5",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Dilithium-5 cryptography",
5
5
  "keywords": [
6
6
  "dilithium",
@@ -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/dilithium5.js --format cjs && rollup src/index.js --file ./dist/mjs/dilithium5.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
@@ -56,7 +56,7 @@ export function cryptoSignKeypair(
56
56
  /**
57
57
  * Create a signature for a message
58
58
  * @param sig - Output buffer for signature (must be CryptoBytes length minimum)
59
- * @param m - Message to sign (hex string or Uint8Array)
59
+ * @param m - Message to sign (hex string or Uint8Array; strings are parsed as hex only)
60
60
  * @param sk - Secret key
61
61
  * @param randomizedSigning - If true, use random nonce; if false, deterministic
62
62
  * @returns 0 on success
@@ -86,7 +86,7 @@ export function cryptoSign(
86
86
  /**
87
87
  * Verify a signature
88
88
  * @param sig - Signature to verify
89
- * @param m - Message that was signed (hex string or Uint8Array)
89
+ * @param m - Message that was signed (hex string or Uint8Array; strings are parsed as hex only)
90
90
  * @param pk - Public key
91
91
  * @returns true if signature is valid, false otherwise
92
92
  */