ths-csprng 1.0.1 → 1.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 CHANGED
@@ -19,7 +19,7 @@ In short: it uses macroscopic messiness as a defense against microscopic observa
19
19
  <br />
20
20
 
21
21
  <div align="center">
22
- <img src="./media/illustration.png" alt="Illustration" width="1250">
22
+ <img src="./media/illustration_1.png" alt="Illustration 1" width="1250">
23
23
  </div>
24
24
 
25
25
  <br />
@@ -28,9 +28,9 @@ In short: it uses macroscopic messiness as a defense against microscopic observa
28
28
 
29
29
  Information doesn’t just vanish (see the **[Quantum No-Hiding Theorem](https://en.wikipedia.org/wiki/No-hiding_theorem)**). It spreads into the environment. If an adversary can reconstruct past states—whether through retrocausality, solving hidden variables, or digging through micro-architectural leaks—traditional seeds become vulnerable.
30
30
 
31
- Normally, a seed is generated at a single point in time (T=0). If that moment is reconstructed, your entropy is gone.
31
+ Normally, a seed is generated at a single point in time (*T*<sub>0</sub>). If that moment is reconstructed, your entropy is gone.
32
32
 
33
- THS fixes this by replacing a single seed with a temporal sequence of many moments (T₁, T₂, …, T). To recover your secret, an attacker doesn’t just need to glance at the past—they have to accurately simulate the entire noisy evolution of the machine across hundreds of layers of timing jitter, memory state, and CPU behavior.
33
+ THS fixes this by replacing a single seed with a temporal sequence of many moments (*T*<sub>1</sub>, *T*<sub>2</sub>, *T*<sub>3</sub>, …, *T*<sub>n</sub>). To recover your secret, an attacker doesn’t just need to glance at the past—they have to accurately simulate the entire noisy evolution of the machine across hundreds of layers of timing jitter, memory state, and CPU behavior.
34
34
 
35
35
  <br />
36
36
 
@@ -48,22 +48,31 @@ npm install ths-csprng
48
48
 
49
49
  ### 🛠️ Quick Start
50
50
 
51
+ #### Modern ESM (Node.js 18+, Vite, Bun)
52
+
51
53
  ```javascript
52
54
  import THS from 'ths-csprng';
53
55
 
56
+ // Standard 256-bit secure random buffer
57
+ const entropy = await THS.random(32);
58
+
59
+ // High-security hardened key
60
+ const masterKey = await THS.random(64, {
61
+ layers: 512, // 512 temporal slices
62
+ harden: 3, // Heavy Argon2id hardening
63
+ mem: 65536, // 64MB memory cost
64
+ trng: true // Try reading directly from /dev/urandom
65
+ });
66
+ ```
67
+
68
+ #### Legacy / Standard CommonJS
69
+
70
+ ```javascript
71
+ const THS = require('ths-csprng');
72
+
54
73
  (async () => {
55
- // Standard 256-bit secure random buffer
56
74
  const entropy = await THS.random(32);
57
-
58
- // High-security hardened key
59
- const masterKey = await THS.random(64, {
60
- layers: 512, // 512 temporal slices
61
- harden: 3, // Heavy Argon2id hardening
62
- mem: 65536, // 64MB memory cost
63
- trng: true // Try reading directly from /dev/urandom
64
- });
65
-
66
- console.log(masterKey.toString('hex'));
75
+ console.log(entropy.toString('hex'));
67
76
  })();
68
77
  ```
69
78
 
@@ -90,12 +99,17 @@ You can dial the security up or down depending on how much “weight” you want
90
99
 
91
100
  ```javascript
92
101
  const options = {
93
- layers: 128, // Number of temporal slices (2 to 65536)
102
+ layers: 128, // Temporal slices (2 to 65536)
94
103
  harden: 2, // 0–3 (hardening depth)
95
- trng: false, // Fallback to internal CSPRNG or /dev/urandom
96
- hashOut: true, // SHA3-512 hash each slice to avoid raw memory leaks
97
- memoryH: 1, // Argon2id iterations (only with harden: 3)
98
- mem: 16384, // Argon2id memory cost in KB (default 16MB)
104
+ trng: false, // Force /dev/urandom usage (w/ fallback)
105
+ buffer: true, // Return Node.js Buffer (false for Uint8Array)
106
+
107
+ // Argon2id Specifics (Requires harden: 3)
108
+ memoryH: 1, // Number of Argon2id cycles per layer
109
+ mem: 16384, // Memory cost in KB (default 16MB)
110
+ passes: 3, // (t) iterations
111
+ parallelism: 4, // (p) parallel threads
112
+
99
113
  label: 'THS-Default-v1' // KMAC personalization string
100
114
  };
101
115
 
@@ -111,10 +125,12 @@ const bytes = await THS.random(32, options);
111
125
  #### Direct Utils
112
126
 
113
127
  ```javascript
128
+ // ESM
114
129
  import Utils from 'ths-csprng/utils';
115
- import THS from 'ths-csprng';
130
+ // CJS
131
+ const Utils = require('ths-csprng/utils');
116
132
 
117
- // Get a raw metabolic snapshot (T=n)
133
+ // Get a raw metabolic snapshot (*T=n*)
118
134
  const snap = await Utils.snapshot(2);
119
135
 
120
136
  // Direct access to the internal TRNG/CSPRNG source
@@ -144,9 +160,15 @@ await THS.rnd(32);
144
160
 
145
161
  ## Why should you care?
146
162
 
147
- Most of the crypto world acts like randomness is a solved problem. But in 2026, with hardware backdoors, VM cloning, and advanced side-channel attacks, standard entropy sources might be more fragile than they look.
163
+ Most of the crypto world acts like randomness is a solved problem. But with hardware backdoors, VM cloning, and advanced side-channel attacks, standard entropy sources might be more fragile than they look.
148
164
 
149
- THS tries to bridge raw hardware chaos with solid cryptographic primitives.
165
+ THS tries to bridge raw hardware chaos with solid cryptographic primitives, forcing 2<sup>n</sup> security to be exactly as 2<sup>n</sup>, not less.
166
+
167
+ <br />
168
+
169
+ <div align="center">
170
+ <img src="./media/illustration_2.png" alt="Illustration 2" width="1250">
171
+ </div>
150
172
 
151
173
  <br />
152
174
 
@@ -169,6 +191,12 @@ THS is aimed at situations where the attacker might try to:
169
191
 
170
192
  <br />
171
193
 
194
+ ## ⚠️ Performance Note
195
+
196
+ **Hardening Level 3** (Heavy) is computationally expensive by design. It is intended for generating long-term master keys or seeds. If your use case requires high-frequency random bytes (e.g., UUIDs or nonces), use **Level 0** or **Level 1**.
197
+
198
+ <br />
199
+
172
200
  ## License
173
201
 
174
202
  Licensed under the **Apache License, Version 2.0**; a robust, permissive license that includes an explicit grant of patent rights and provides protection against contributor liability.
package/package.json CHANGED
@@ -1,12 +1,23 @@
1
1
  {
2
2
  "name": "ths-csprng",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Temporal-Hardening Solution: A CSPRNG wrapper designed to resist historical state reconstruction and hardware backdoors.",
5
- "type": "module",
6
5
  "main": "./src/index.js",
6
+ "files": [
7
+ "src",
8
+ "LICENSE",
9
+ "README.md",
10
+ "npm-shrinkwrap.json"
11
+ ],
7
12
  "exports": {
8
- ".": "./src/index.js",
9
- "./utils": "./src/util.js"
13
+ ".": {
14
+ "import": "./src/index.mjs",
15
+ "require": "./src/index.js"
16
+ },
17
+ "./utils": {
18
+ "import": "./src/util.mjs",
19
+ "require": "./src/util.js"
20
+ }
10
21
  },
11
22
  "author": "Aries Harbinger",
12
23
  "license": "Apache-2.0",
package/src/index.js CHANGED
@@ -16,15 +16,15 @@
16
16
  * limitations under the License.
17
17
  */
18
18
 
19
- import Utils from './util.js';
20
- import { kmac256 } from '@noble/hashes/sha3-addons.js';
21
- import { createHash, webcrypto } from 'node:crypto';
19
+ const Utils = require('./util.js');
20
+ const { kmac256 } = require('@noble/hashes/sha3-addons.js');
21
+ const { webcrypto } = require('node:crypto');
22
22
 
23
23
 
24
24
  /**
25
25
  * Temporal-Hardening Solution - Applied in Random Number Generator
26
26
  */
27
- export const THS = Object.freeze({
27
+ const THS = Object.freeze({
28
28
  /**
29
29
  * Generates cryptographically secure random bytes with temporal hardening.
30
30
  */
@@ -33,10 +33,12 @@ export const THS = Object.freeze({
33
33
  layers = 128, // number of timeline slices (iterations)
34
34
  harden = 2, // levels: 0 (hrtime), 1 (+entropy), 2 (+internal), 3 (+Argon2id)
35
35
  trng = false, // attempts to read /dev/urandom, fallback to randomBytes()
36
- hashOut = true, // whether to hash every moments instead of processing it as raw data
36
+ buffer = true, // return as buffer
37
37
 
38
38
  memoryH = 1, // number of Argon2id runs (requires harden=3 level)
39
39
  mem = 16384, // memory hardness of Argon2id (default: 16MB)
40
+ passes = 3, // (t) passes for Argon2id (default: 3)
41
+ parallelism = 4, // (p) parallelism for Argon2id (default: 4)
40
42
  label = 'THS-Default-v1' // kmac label
41
43
 
42
44
  } = {}) {
@@ -56,56 +58,40 @@ export const THS = Object.freeze({
56
58
  if ('number' !== typeof mem || mem < 1024)
57
59
  throw new Error(`Memory set [${mem}] for Argon2id must be a valid number and not be less than 1024 or 1KB.`);
58
60
 
59
-
60
- // Collector for metabolic moments (The Message)
61
- const moments = [];
62
- let seed;
61
+ const seed = await Utils.getRandom(32, trng);
62
+ let k;
63
63
 
64
64
  try {
65
- for (let i = 0; i < layers; i++) {
66
- const moment = await Utils.snapshot(harden, memoryH, mem, i, trng);
67
-
68
- if (!hashOut) { moments.push(moment); }
69
-
70
- else {
71
- const h = createHash('sha3-512') // Structurally immune to length-extension attacks
72
- .update(moment)
73
- .digest();
65
+ k = kmac256.create(seed, {
66
+ personalization: Utils.toBytes(label),
67
+ dkLen: length
68
+ });
74
69
 
75
- moment.fill(0); // immediate wipe
76
- moments.push(h);
77
- }
70
+ for (let i = 0; i < layers; i++) {
71
+ const moment = await Utils.snapshot(harden, memoryH, mem, passes, parallelism, i, trng);
72
+ k.update(moment), moment.fill(0);
78
73
 
79
- // Supplemental entropy injected into the moment collection
80
74
  if (harden >= 1) {
75
+ // Supplemental entropy injected into the moment collection
81
76
  const sup = await Utils.getRandom(12, trng);
82
- moments.push(sup);
77
+ k.update(sup), sup.fill(0);
83
78
  }
84
79
 
85
80
  if (i < layers - 1) await Utils.jitter(trng);
86
81
  }
87
82
 
88
- // Initial KMAC Key
89
- seed = await Utils.getRandom(32, trng);
90
-
91
- // Finalize with KMAC256
92
- const result = kmac256(seed, Buffer.concat(moments), {
93
- personalization: Utils.toBytes(label),
94
- dkLen: length
95
- });
83
+ const out = k.digest();
84
+ if (!buffer) return out;
96
85
 
97
- return Buffer.from(result);
86
+ const b = Buffer.from(out);
87
+ return out.fill(0), b;
98
88
  }
99
89
 
100
- catch (e) { throw e }
101
-
102
- finally {
103
- // Cleanup sensitive memory
104
- seed && seed.fill(0);
90
+ catch (e) { throw e; }
105
91
 
106
- for (const m of moments) {
107
- (m && typeof m.fill === 'function') && m.fill(0);
108
- }
92
+ finally {
93
+ seed.fill(0);
94
+ k = null;
109
95
  }
110
96
  },
111
97
 
@@ -113,19 +99,18 @@ export const THS = Object.freeze({
113
99
  rnd (len, o) { return THS.random(len, o) },
114
100
 
115
101
  // Direct use of /dev/urandom (fallback to randomBytes)
116
- async raw(len) { return await Utils.getRandom(len, true) },
102
+ async raw(len) { return await Utils.getRandom(len, true) },
117
103
 
118
104
  // Direct Webcrypto access
119
- fillRandom(arr) {
120
- return (
121
- (typeof window !== 'undefined' && window.crypto)
122
- ? window.crypto
123
- : webcrypto
124
- ).getRandomValues(arr)
105
+ fillRandom(arr) {
106
+ return (
107
+ (typeof window !== 'undefined' && window.crypto)
108
+ ? window.crypto
109
+ : webcrypto
110
+ ).getRandomValues(arr)
125
111
  }
126
112
  });
127
113
 
128
- // Must use await
129
- export const randomBytes = (len, o) => THS.random(len, o);
130
-
131
- export default THS;
114
+ module.exports = THS;
115
+ module.exports.THS = THS;
116
+ module.exports.randomBytes = (len, o) => THS.random(len, o);
package/src/index.mjs ADDED
@@ -0,0 +1,117 @@
1
+ /**
2
+ * THS (Temporal-Hardening Solution)
3
+ *
4
+ * Copyright 2026 Aries Harbinger
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+
19
+ import Utils from './util.mjs';
20
+ import { kmac256 } from '@noble/hashes/sha3-addons.js';
21
+ import { createHash, webcrypto } from 'node:crypto';
22
+
23
+
24
+ /**
25
+ * Temporal-Hardening Solution - Applied in Random Number Generator
26
+ */
27
+ export const THS = Object.freeze({
28
+ /**
29
+ * Generates cryptographically secure random bytes with temporal hardening.
30
+ */
31
+ async random(length = 32, {
32
+
33
+ layers = 128, // number of timeline slices (iterations)
34
+ harden = 2, // levels: 0 (hrtime), 1 (+entropy), 2 (+internal), 3 (+Argon2id)
35
+ trng = false, // attempts to read /dev/urandom, fallback to randomBytes()
36
+ buffer = true, // return as buffer
37
+
38
+ memoryH = 1, // number of Argon2id runs (requires harden=3 level)
39
+ mem = 16384, // memory hardness of Argon2id (default: 16MB)
40
+ passes = 3, // (t) passes for Argon2id (default: 3)
41
+ parallelism = 4, // (p) parallelism for Argon2id (default: 4)
42
+ label = 'THS-Default-v1' // kmac label
43
+
44
+ } = {}) {
45
+
46
+ if (length <= 0)
47
+ return Buffer.alloc(0);
48
+
49
+ if (layers < 2 || layers > 65536)
50
+ throw new Error(`Layer count [${layers}] is invalid. Requires a temporal span of 2-65536 cycles to ensure metabolic variance.`);
51
+
52
+ if (harden < 0 || harden > 3)
53
+ throw new Error(`Hardening level [${harden}] is unknown. Use 0 (Basic) through 3 (Heavy).`);
54
+
55
+ if (harden >= 3 && (memoryH < 1 || memoryH > layers))
56
+ throw new Error(`Memory hardening cycle count [${memoryH}] exceeds available temporal layers [${layers}].`);
57
+
58
+ if ('number' !== typeof mem || mem < 1024)
59
+ throw new Error(`Memory set [${mem}] for Argon2id must be a valid number and not be less than 1024 or 1KB.`);
60
+
61
+ const seed = await Utils.getRandom(32, trng);
62
+ let k;
63
+
64
+ try {
65
+ k = kmac256.create(seed, {
66
+ personalization: Utils.toBytes(label),
67
+ dkLen: length
68
+ });
69
+
70
+ for (let i = 0; i < layers; i++) {
71
+ const moment = await Utils.snapshot(harden, memoryH, mem, passes, parallelism, i, trng);
72
+ k.update(moment), moment.fill(0);
73
+
74
+ if (harden >= 1) {
75
+ // Supplemental entropy injected into the moment collection
76
+ const sup = await Utils.getRandom(12, trng);
77
+ k.update(sup), sup.fill(0);
78
+ }
79
+
80
+ if (i < layers - 1) await Utils.jitter(trng);
81
+ }
82
+
83
+ const out = k.digest();
84
+ if (!buffer) return out;
85
+
86
+ const b = Buffer.from(out);
87
+ return out.fill(0), b;
88
+ }
89
+
90
+ catch (e) { throw e; }
91
+
92
+ finally {
93
+ seed.fill(0);
94
+ k = null;
95
+ }
96
+ },
97
+
98
+ rand(len, o) { return THS.random(len, o) },
99
+ rnd (len, o) { return THS.random(len, o) },
100
+
101
+ // Direct use of /dev/urandom (fallback to randomBytes)
102
+ async raw(len) { return await Utils.getRandom(len, true) },
103
+
104
+ // Direct Webcrypto access
105
+ fillRandom(arr) {
106
+ return (
107
+ (typeof window !== 'undefined' && window.crypto)
108
+ ? window.crypto
109
+ : webcrypto
110
+ ).getRandomValues(arr)
111
+ }
112
+ });
113
+
114
+ // Must use await
115
+ export const randomBytes = (len, o) => THS.random(len, o);
116
+
117
+ export default THS;
package/src/util.js CHANGED
@@ -16,28 +16,27 @@
16
16
  * limitations under the License.
17
17
  */
18
18
 
19
- import fs from 'node:fs/promises';
20
- import { promisify } from 'node:util';
21
- import { randomBytes, timingSafeEqual, argon2 as _argon2 } from 'node:crypto';
19
+ const fs = require('node:fs/promises');
20
+ const { promisify } = require('node:util');
21
+ const { randomBytes, timingSafeEqual, createHash, argon2: _argon2 } = require('node:crypto');
22
22
 
23
23
  const argon2 = promisify(_argon2);
24
24
  const defined_past = process.hrtime.bigint();
25
25
 
26
26
 
27
- export const Utils = Object.freeze({
27
+ const Utils = Object.freeze({
28
28
  /**
29
29
  * Captures a high-resolution snapshot of the Node.js metabolic state.
30
30
  * Uses concatenation to prevent entropy collisions.
31
31
  */
32
- async snapshot(h, mh, mem, s, useUrandom) {
32
+ async snapshot(h, mh, mem, t, p, s, useUrandom) {
33
33
  // Level 1: Minimal data
34
34
  if (h <= 1) return this.bigIntToBuffer(process.hrtime.bigint());
35
35
 
36
36
  const u = process.resourceUsage();
37
37
  const m = process.memoryUsage();
38
38
 
39
- // Primary metabolic collection (Machine State)
40
- // We use an array to preserve individual entropy of each metric
39
+ // Level 2: Primary metabolic collection (Machine State)
41
40
  const metrics = [
42
41
  process.hrtime.bigint(), // CPU Nanoseconds
43
42
  BigInt(Math.round(process.uptime() * 1_000_000)),
@@ -58,38 +57,56 @@ export const Utils = Object.freeze({
58
57
  BigInt(s || 0) // Sequential salt
59
58
  ].map(n => this.bigIntToBuffer(n));
60
59
 
61
- // Map metrics to uniquely structured buffers
62
- const b = Buffer.concat([
60
+ // 128 bytes of raw, uninitialized system memory
61
+ // (May contain fragments of previous internal function calls/pointers, which is an additional entropy source)
62
+ const noise = Buffer.allocUnsafe(128);
63
63
 
64
- // Inject 128 bytes of raw, uninitialized system memory
65
- // (May contain fragments of previous internal function calls/pointers)
66
- Buffer.allocUnsafe(128),
64
+ // Hash that insecure system memory with SHA3-512
65
+ const hashedNoise = createHash('sha3-512')
66
+ .update(noise)
67
+ .update(process.hrtime.bigint().toString())
68
+ .digest();
67
69
 
70
+ // Wipe the noise immediately
71
+ noise.fill(0);
72
+
73
+ // Map metrics to uniquely structured buffers
74
+ const b = Buffer.concat([
75
+ hashedNoise, // This is a COPY of the digest
68
76
  ...metrics
69
77
  ]);
78
+ hashedNoise.fill(0); // Wipe the hashed noise
70
79
 
71
80
  if (h <= 2 || s >= mh) return b;
72
81
 
73
82
  // Level 3: Argon2id Memory-Hardening
74
- const nonce = await this.getRandom(16, useUrandom);
83
+ let nonce, memHard;
75
84
 
76
85
  try {
77
- const memHard = await argon2('argon2id', {
86
+ nonce = await this.getRandom(16, useUrandom);
87
+ memHard = await argon2('argon2id', {
78
88
  nonce,
79
89
  message: b,
80
90
  memory: mem,
81
- passes: 3,
82
- parallelism: 4,
91
+ passes: t,
92
+ parallelism: p,
83
93
  tagLength: 64
84
94
  });
85
95
 
86
- return (h <= 3)
87
- ? Buffer.concat([b, memHard])
88
- : memHard;
96
+ const result = Buffer.concat([memHard, b]);
97
+ if (h <= 3) return result;
98
+
99
+ throw new Error(`Hardening level [${h}] is unknown. Use 0 (Basic) through 3 (Heavy).`);
89
100
  }
90
101
 
91
- catch (e) { throw e }
92
- finally { nonce.fill(0); }
102
+ catch (e) { throw e; }
103
+
104
+ finally {
105
+ // wipe result
106
+ if (memHard) memHard.fill(0);
107
+ if (nonce) nonce.fill(0);
108
+ b.fill(0);
109
+ }
93
110
  },
94
111
 
95
112
  /**
@@ -166,4 +183,5 @@ export const Utils = Object.freeze({
166
183
  }
167
184
  });
168
185
 
169
- export default Utils;
186
+ module.exports = Utils;
187
+ module.exports.Utils = Utils;
package/src/util.mjs ADDED
@@ -0,0 +1,186 @@
1
+ /**
2
+ * THS (Temporal-Hardening Solution)
3
+ *
4
+ * Copyright 2026 Aries Harbinger
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+
19
+ import fs from 'node:fs/promises';
20
+ import { promisify } from 'node:util';
21
+ import { randomBytes, timingSafeEqual, createHash, argon2 as _argon2 } from 'node:crypto';
22
+
23
+ const argon2 = promisify(_argon2);
24
+ const defined_past = process.hrtime.bigint();
25
+
26
+
27
+ export const Utils = Object.freeze({
28
+ /**
29
+ * Captures a high-resolution snapshot of the Node.js metabolic state.
30
+ * Uses concatenation to prevent entropy collisions.
31
+ */
32
+ async snapshot(h, mh, mem, t, p, s, useUrandom) {
33
+ // Level 1: Minimal data
34
+ if (h <= 1) return this.bigIntToBuffer(process.hrtime.bigint());
35
+
36
+ const u = process.resourceUsage();
37
+ const m = process.memoryUsage();
38
+
39
+ // Level 2: Primary metabolic collection (Machine State)
40
+ const metrics = [
41
+ process.hrtime.bigint(), // CPU Nanoseconds
42
+ BigInt(Math.round(process.uptime() * 1_000_000)),
43
+ BigInt(u.userCPUTime), // User-space CPU usage
44
+ BigInt(u.systemCPUTime), // Kernel-space CPU usage
45
+ BigInt(u.minorPageFault), // Soft memory faults
46
+ BigInt(u.majorPageFault), // Hard I/O faults
47
+ BigInt(u.voluntaryContextSwitches),
48
+ BigInt(u.involuntaryContextSwitches),
49
+ BigInt(m.heapUsed), // V8 actual usage
50
+ BigInt(m.heapTotal), // V8 allocated total
51
+ BigInt(m.external), // C++ object memory
52
+ BigInt(m.arrayBuffers || 0), // Raw buffer memory
53
+ BigInt(m.rss), // Resident Set Size (Physical RAM)
54
+ BigInt(Math.round(performance.now() * 1_000_000)),
55
+ defined_past, // Process start anchor
56
+ BigInt(Date.now()), // Wall clock (Temporal anchor)
57
+ BigInt(s || 0) // Sequential salt
58
+ ].map(n => this.bigIntToBuffer(n));
59
+
60
+ // 128 bytes of raw, uninitialized system memory
61
+ // (May contain fragments of previous internal function calls/pointers, which is an additional entropy source)
62
+ const noise = Buffer.allocUnsafe(128);
63
+
64
+ // Hash that insecure system memory with SHA3-512
65
+ const hashedNoise = createHash('sha3-512')
66
+ .update(noise)
67
+ .update(process.hrtime.bigint().toString())
68
+ .digest();
69
+
70
+ // Wipe the noise immediately
71
+ noise.fill(0);
72
+
73
+ // Map metrics to uniquely structured buffers
74
+ const b = Buffer.concat([
75
+ hashedNoise, // This is a COPY of the digest
76
+ ...metrics
77
+ ]);
78
+ hashedNoise.fill(0); // Wipe the hashed noise
79
+
80
+ if (h <= 2 || s >= mh) return b;
81
+
82
+ // Level 3: Argon2id Memory-Hardening
83
+ let nonce, memHard;
84
+
85
+ try {
86
+ nonce = await this.getRandom(16, useUrandom);
87
+ memHard = await argon2('argon2id', {
88
+ nonce,
89
+ message: b,
90
+ memory: mem,
91
+ passes: t,
92
+ parallelism: p,
93
+ tagLength: 64
94
+ });
95
+
96
+ const result = Buffer.concat([memHard, b]);
97
+ if (h <= 3) return result;
98
+
99
+ throw new Error(`Hardening level [${h}] is unknown. Use 0 (Basic) through 3 (Heavy).`);
100
+ }
101
+
102
+ catch (e) { throw e; }
103
+
104
+ finally {
105
+ // wipe result
106
+ if (memHard) memHard.fill(0);
107
+ if (nonce) nonce.fill(0);
108
+ b.fill(0);
109
+ }
110
+ },
111
+
112
+ /**
113
+ * Attempts to harvest raw entropy from the OS device.
114
+ * Fallback to hardware-backed Node.js randomBytes on failure.
115
+ */
116
+ async getRandom(len, useUrandom) {
117
+ if (!useUrandom) return randomBytes(len);
118
+
119
+ let fd;
120
+ try {
121
+ fd = await fs.open('/dev/urandom', 'r');
122
+ const buffer = Buffer.allocUnsafe(len);
123
+ const { bytesRead } = await fd.read(buffer, 0, len, 0);
124
+
125
+ // If we didn't read enough bytes for some reason, fallback
126
+ return (bytesRead < len)
127
+ ? randomBytes(len)
128
+ : buffer;
129
+ }
130
+
131
+ catch (e) { return randomBytes(len); }
132
+ finally { if (fd) await fd.close(); }
133
+ },
134
+
135
+ /**
136
+ * Micro-architectural noise generator.
137
+ */
138
+ async jitter(useUrandom) {
139
+ const b = await this.getRandom(64, useUrandom);
140
+ timingSafeEqual(b, b);
141
+ b.fill(0);
142
+ },
143
+
144
+ /**
145
+ * Optimized BigInt to Buffer conversion with 16-bit Length Header.
146
+ * Prepends a 2-byte length header (Big-Endian) followed by the data.
147
+ */
148
+ bigIntToBuffer(n) {
149
+ if (typeof n !== 'bigint') n = BigInt(n);
150
+ if (n < 0n) throw new RangeError('Negative values not supported');
151
+
152
+ // Fast path for zero: [Length: 0x0001] [Value: 0x00]
153
+ if (n === 0n) {
154
+ const buf = Buffer.allocUnsafe(3);
155
+ buf.writeUInt16BE(1, 0); // Length is 1 byte
156
+ buf[2] = 0; // Value is 0
157
+ return buf;
158
+ }
159
+
160
+ // Convert BigInt to Buffer via Hex
161
+ let hex = n.toString(16);
162
+ if (hex.length % 2 !== 0) hex = '0' + hex;
163
+ const data = Buffer.from(hex, 'hex');
164
+
165
+ // Safety check for 16-bit overflow
166
+ if (data.length > 0xFFFF)
167
+ throw new RangeError('BigInt exceeds 16-bit length header capacity');
168
+
169
+ // Structure: [Header (2 bytes)] + [Data]
170
+ const header = Buffer.allocUnsafe(2);
171
+ header.writeUInt16BE(data.length, 0);
172
+
173
+ return Buffer.concat([header, data]);
174
+ },
175
+
176
+ /**
177
+ * Normalizes various input types into a Uint8Array.
178
+ */
179
+ toBytes(input) {
180
+ if (input instanceof Uint8Array) return input;
181
+ if (typeof input === 'string') return new TextEncoder().encode(input);
182
+ return new TextEncoder().encode(JSON.stringify(input) || '');
183
+ }
184
+ });
185
+
186
+ export default Utils;