ths-csprng 1.0.0 → 1.0.2

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
@@ -1,90 +1,132 @@
1
- <div align="center">
2
- <img src="./media/banner.png" alt="Banner" width="1250">
3
- </div>
1
+ # THS (Temporal-Hardening Solution)
2
+
3
+ THS is a **security-hardened CSPRNG wrapper** designed to protect cryptographic seeds against historical state reconstruction and hardware backdoors. It replaces single-point-in-time entropy with a temporal sequence of "metabolic" system noise, forcing adversaries to perform massive physical work to recover keys.
4
+
5
+ <br />
6
+
7
+ ---
4
8
 
5
9
  <br />
6
10
 
7
- Standard RNGs are built on the naive assumption that once a seed is "randomized," the history of its creation just... disappears.
11
+ ### 🚀 Quick Start
8
12
 
9
- In reality, if an observer can violate causality or reconstruct the precise physical state of a system, your "randomness" is just an entry in a ledger.
13
+ ```bash
14
+ npm install ths-csprng
15
+ ```
10
16
 
11
17
  <br />
12
18
 
13
- If you are on to a **Block Universe** or **Superdeterministic** model, your "random" seed is just a static coordinate in spacetime. To an observer with enough compute or a vantage point further down the temporal axis, your private key isn't a secret — it’s a historical fact.
19
+ #### ESM (Node.js 18+, Vite, Bun)
14
20
 
15
- **THS (Temporal-Hardening Solution)** is an attempt to fix this. It is a cryptographic wrapper that forces an adversary to move from attacking logic to attacking physical work, which makes “retrocausal observation” or state reconstruction exponentially challenging.
21
+ ```javascript
22
+ import THS from 'ths-csprng';
16
23
 
17
- To put it simply, it is a “Macroscopic Chaos as a defense against Microscopic Observation.”
24
+ // Standard 256-bit secure random buffer
25
+ const entropy = await THS.random(32);
18
26
 
19
- <br />
27
+ // High-security hardened key (Computational work required to reconstruct)
28
+ const masterKey = await THS.random(64, {
29
+ layers: 512, // 512 temporal slices
30
+ harden: 3, // Heavy Argon2id hardening
31
+ mem: 65536 // 64MB memory cost
32
+ });
33
+ ```
20
34
 
21
- ---
35
+ #### CommonJS
22
36
 
23
- <br />
37
+ ```javascript
38
+ const THS = require('ths-csprng');
24
39
 
25
- <div align="center">
26
- <img src="./media/illustration.png" alt="Illustration" width="1250">
27
- </div>
40
+ (async () => {
41
+ const entropy = await THS.random(32);
42
+ console.log(entropy.toString('hex'));
43
+ })();
44
+ ```
28
45
 
29
46
  <br />
30
47
 
31
- ## The "Observer from the Future" Problem
48
+ ---
32
49
 
33
- Information doesn't just vanish (shout out to the **[Quantum No-Hiding Theorem](https://en.wikipedia.org/wiki/No-hiding_theorem)**). It leaks into the environment. If someone compromises your hardware or develops a way to reconstruct historical system states, your static seeds are toast.
50
+ ### 💎 Hardening Levels
34
51
 
35
- THS creates **Computational Fog**. Instead of a mathematical point, it binds your secret to the "metabolic noise" of your CPU at a specific nanosecond.
52
+ | Level | Mode | Description |
53
+ | :--- | :--- | :--- |
54
+ | **0** | **Basic** | High-resolution hardware timing (`hrtime`) per slice. |
55
+ | **1** | **Entropy** | Level 0 + supplemental CSPRNG entropy injection. |
56
+ | **2** | **Internal** | Level 1 + metabolic collection (Internal state snapshots). |
57
+ | **3** | **Heavy** | Level 2 + **Argon2id** memory-hardened processing. |
36
58
 
37
59
  <br />
38
60
 
39
- ### How it actually works:
61
+ ---
62
+
63
+ ### ⚙️ Full Configuration
40
64
 
41
- * **Micro-Architectural Jitter:** Harvesting the tiny, unpredictable timing variances in your CPU clock caused by interrupts and thread scheduling.
42
- * **Physical Binding:** Capturing "ghost data" from uninitialized memory, we inject artifacts of the machine's immediate past into the entropy pool.
43
- * **Memory-Hardened Anchors:** We use **Argon2id** to spin up a shifting memory state. This forces an attacker to simulate a massive slice of physical RAM history just to see what happened in that one millisecond.
65
+ ```javascript
66
+ const bytes = await THS.random(32, {
67
+ layers: 128, // Temporal slices (2 to 65536)
68
+ harden: 2, // 0–3 (hardening depth)
69
+ trng: false, // Force /dev/urandom usage
70
+ buffer: true, // Return Node.js Buffer
71
+
72
+ // Argon2id Specifics (Required for level 3)
73
+ mem: 16384, // Memory cost in KB
74
+ passes: 3, // Iterations
75
+ parallelism: 4 // Threads
76
+ });
77
+ ```
44
78
 
45
79
  <br />
46
80
 
47
81
  ---
48
82
 
49
- <br />
83
+ ### 🧬 Advanced API
50
84
 
51
- ## Getting Started
85
+ #### Direct Utils
52
86
 
53
- ```bash
54
- npm install ths-csprng
87
+ ```javascript
88
+ // ESM
89
+ import Utils from 'ths-csprng/utils';
90
+ // CJS
91
+ const Utils = require('ths-csprng/utils');
92
+
93
+ // Get a raw metabolic snapshot (*T=n*)
94
+ const snap = await Utils.snapshot(2);
95
+
96
+ // Direct access to the internal TRNG/CSPRNG source
97
+ const raw = await THS.raw(32);
55
98
  ```
56
99
 
57
- <br />
58
100
 
59
- ### Quick Start
101
+ #### Browser Support
60
102
 
61
103
  ```javascript
62
- import THS from 'ths-csprng';
104
+ const buf = new Uint8Array(32);
105
+ THS.fillRandom(buf);
106
+ ```
63
107
 
64
- (async () => {
65
- // Get a 512-bit seed tied to this specific moment in hardware history
66
- const seed = await THS.random(64);
67
-
68
- // Deep hardening for keys meant to outlast the current paradigm
69
- const key = await THS.random(32, {
70
- layers: 500, // Increase the temporal iterations (slices)
71
- harden: 3, // Full metabolic collection + Argon2id
72
- mem: 256 * 1024, // Prevents the "Cold Start Problem"
73
- memoryH: 1, // Run Argon2id at once
74
- trng: true // Use randomness from /dev/urandom directly (Unix-like only)
75
- });
76
- })();
108
+
109
+ #### Convenient Aliases
110
+
111
+ ```javascript
112
+ import { randomBytes } from 'ths-csprng';
113
+
114
+ await randomBytes(32);
115
+ await THS.rand(32);
116
+ await THS.rnd(32);
77
117
  ```
78
118
 
79
119
  <br />
80
120
 
81
- ## Why should you care?
82
-
83
- Most people **don't need this**.
121
+ ---
84
122
 
85
- If you're building a todo app, move on. If you believe that the future cannot affect the past, that hardware manufacturers never lie, and that “random” numbers are truly disconnected from the physical state of the machine, then this tool is just a very complex way to waste CPU cycles.
123
+ ### 🛡️ Threat Model
124
+ THS is designed for high-stakes environments where an attacker might:
125
+ 1. **Reconstruct historical states** of a machine to guess seeds.
126
+ 2. **Exploit VM Snapshots** where cloned instances produce identical "random" output.
127
+ 3. **Bypass Hardware Backdoors** by mixing high-variance "metabolic" noise into the entropy pool.
86
128
 
87
- Temporal-Hardening Solution (THS) is designed for high-integrity cryptographic secrets where the threat model includes **historical state reconstruction** or **backdoored hardware**. It is a defense against the reality that even if your randomness is 100% deterministic within a closed system, it must be **Computationally Irreducible** to an outsider.
129
+ **Note:** Hardening Level 3 is computationally expensive. Use Level 0 or 1 for high-frequency needs like UUIDs or nonces.
88
130
 
89
131
  <br />
90
132
 
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "ths-csprng",
3
+ "version": "1.0.2",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "ths-csprng",
9
+ "version": "1.0.2",
10
+ "license": "Apache-2.0",
11
+ "dependencies": {
12
+ "@noble/hashes": "^2.2.0"
13
+ },
14
+ "engines": {
15
+ "node": ">=18"
16
+ }
17
+ },
18
+ "node_modules/@noble/hashes": {
19
+ "version": "2.2.0",
20
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz",
21
+ "integrity": "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==",
22
+ "license": "MIT",
23
+ "engines": {
24
+ "node": ">= 20.19.0"
25
+ },
26
+ "funding": {
27
+ "url": "https://paulmillr.com/funding/"
28
+ }
29
+ }
30
+ }
31
+ }
package/package.json CHANGED
@@ -1,11 +1,23 @@
1
1
  {
2
2
  "name": "ths-csprng",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
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"
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
+ }
9
21
  },
10
22
  "author": "Aries Harbinger",
11
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 } 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 = {
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 = {
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,69 +58,59 @@ export const THS = {
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);
83
+ const out = k.digest();
84
+ if (!buffer) return out;
90
85
 
91
- // Finalize with KMAC256
92
- const result = kmac256(seed, Buffer.concat(moments), {
93
- personalization: Utils.toBytes(label),
94
- dkLen: length
95
- });
96
-
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
 
112
- async rand(len, o) { return await THS.random(len, o) },
113
- async rnd (len, o) { return await THS.random(len, o) },
114
-
115
- // direct use raw random (fallback to randomBytes)
116
- async raw(len) { return await Utils.getRandom(len, true) }
117
- };
118
-
119
- // Must use await
120
- export const randomBytes = async (len, o) => {
121
- return await THS.random(len, o);
122
- };
123
-
124
- export default THS;
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
+ 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,31 +16,30 @@
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, 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 = {
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
- BigInt(Math.round(process.uptime() * 1_000_000)),
42
+ BigInt(Math.round(process.uptime() * 1_000_000)),
44
43
  BigInt(u.userCPUTime), // User-space CPU usage
45
44
  BigInt(u.systemCPUTime), // Kernel-space CPU usage
46
45
  BigInt(u.minorPageFault), // Soft memory faults
@@ -55,7 +54,7 @@ export const Utils = {
55
54
  BigInt(Math.round(performance.now() * 1_000_000)),
56
55
  defined_past, // Process start anchor
57
56
  BigInt(Date.now()), // Wall clock (Temporal anchor)
58
- BigInt(s) // Sequential salt
57
+ BigInt(s || 0) // Sequential salt
59
58
  ].map(n => this.bigIntToBuffer(n));
60
59
 
61
60
  // Map metrics to uniquely structured buffers
@@ -71,25 +70,33 @@ export const Utils = {
71
70
  if (h <= 2 || s >= mh) return b;
72
71
 
73
72
  // Level 3: Argon2id Memory-Hardening
74
- const nonce = await this.getRandom(16, useUrandom);
73
+ let nonce, memHard;
75
74
 
76
75
  try {
77
- const memHard = await argon2('argon2id', {
76
+ nonce = await this.getRandom(16, useUrandom);
77
+ memHard = await argon2('argon2id', {
78
78
  nonce,
79
79
  message: b,
80
80
  memory: mem,
81
- passes: 3,
82
- parallelism: 4,
81
+ passes: t,
82
+ parallelism: p,
83
83
  tagLength: 64
84
84
  });
85
85
 
86
- return (h <= 3)
87
- ? Buffer.concat([b, memHard])
88
- : memHard;
86
+ const result = Buffer.concat([memHard, b]);
87
+ if (h <= 3) return result;
88
+
89
+ throw new Error(`Hardening level [${h}] is unknown. Use 0 (Basic) through 3 (Heavy).`);
89
90
  }
90
91
 
91
- catch (e) { throw e }
92
- finally { nonce.fill(0); }
92
+ catch (e) { throw e; }
93
+
94
+ finally {
95
+ // wipe result
96
+ if (memHard) memHard.fill(0);
97
+ if (nonce) nonce.fill(0);
98
+ b.fill(0);
99
+ }
93
100
  },
94
101
 
95
102
  /**
@@ -164,6 +171,7 @@ export const Utils = {
164
171
  if (typeof input === 'string') return new TextEncoder().encode(input);
165
172
  return new TextEncoder().encode(JSON.stringify(input) || '');
166
173
  }
167
- };
174
+ });
168
175
 
169
- export default Utils;
176
+ module.exports = Utils;
177
+ module.exports.Utils = Utils;
package/src/util.mjs ADDED
@@ -0,0 +1,176 @@
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, 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
+ // Map metrics to uniquely structured buffers
61
+ const b = Buffer.concat([
62
+
63
+ // Inject 128 bytes of raw, uninitialized system memory
64
+ // (May contain fragments of previous internal function calls/pointers)
65
+ Buffer.allocUnsafe(128),
66
+
67
+ ...metrics
68
+ ]);
69
+
70
+ if (h <= 2 || s >= mh) return b;
71
+
72
+ // Level 3: Argon2id Memory-Hardening
73
+ let nonce, memHard;
74
+
75
+ try {
76
+ nonce = await this.getRandom(16, useUrandom);
77
+ memHard = await argon2('argon2id', {
78
+ nonce,
79
+ message: b,
80
+ memory: mem,
81
+ passes: t,
82
+ parallelism: p,
83
+ tagLength: 64
84
+ });
85
+
86
+ const result = Buffer.concat([memHard, b]);
87
+ if (h <= 3) return result;
88
+
89
+ throw new Error(`Hardening level [${h}] is unknown. Use 0 (Basic) through 3 (Heavy).`);
90
+ }
91
+
92
+ catch (e) { throw e; }
93
+
94
+ finally {
95
+ // wipe result
96
+ if (memHard) memHard.fill(0);
97
+ if (nonce) nonce.fill(0);
98
+ b.fill(0);
99
+ }
100
+ },
101
+
102
+ /**
103
+ * Attempts to harvest raw entropy from the OS device.
104
+ * Fallback to hardware-backed Node.js randomBytes on failure.
105
+ */
106
+ async getRandom(len, useUrandom) {
107
+ if (!useUrandom) return randomBytes(len);
108
+
109
+ let fd;
110
+ try {
111
+ fd = await fs.open('/dev/urandom', 'r');
112
+ const buffer = Buffer.allocUnsafe(len);
113
+ const { bytesRead } = await fd.read(buffer, 0, len, 0);
114
+
115
+ // If we didn't read enough bytes for some reason, fallback
116
+ return (bytesRead < len)
117
+ ? randomBytes(len)
118
+ : buffer;
119
+ }
120
+
121
+ catch (e) { return randomBytes(len); }
122
+ finally { if (fd) await fd.close(); }
123
+ },
124
+
125
+ /**
126
+ * Micro-architectural noise generator.
127
+ */
128
+ async jitter(useUrandom) {
129
+ const b = await this.getRandom(64, useUrandom);
130
+ timingSafeEqual(b, b);
131
+ b.fill(0);
132
+ },
133
+
134
+ /**
135
+ * Optimized BigInt to Buffer conversion with 16-bit Length Header.
136
+ * Prepends a 2-byte length header (Big-Endian) followed by the data.
137
+ */
138
+ bigIntToBuffer(n) {
139
+ if (typeof n !== 'bigint') n = BigInt(n);
140
+ if (n < 0n) throw new RangeError('Negative values not supported');
141
+
142
+ // Fast path for zero: [Length: 0x0001] [Value: 0x00]
143
+ if (n === 0n) {
144
+ const buf = Buffer.allocUnsafe(3);
145
+ buf.writeUInt16BE(1, 0); // Length is 1 byte
146
+ buf[2] = 0; // Value is 0
147
+ return buf;
148
+ }
149
+
150
+ // Convert BigInt to Buffer via Hex
151
+ let hex = n.toString(16);
152
+ if (hex.length % 2 !== 0) hex = '0' + hex;
153
+ const data = Buffer.from(hex, 'hex');
154
+
155
+ // Safety check for 16-bit overflow
156
+ if (data.length > 0xFFFF)
157
+ throw new RangeError('BigInt exceeds 16-bit length header capacity');
158
+
159
+ // Structure: [Header (2 bytes)] + [Data]
160
+ const header = Buffer.allocUnsafe(2);
161
+ header.writeUInt16BE(data.length, 0);
162
+
163
+ return Buffer.concat([header, data]);
164
+ },
165
+
166
+ /**
167
+ * Normalizes various input types into a Uint8Array.
168
+ */
169
+ toBytes(input) {
170
+ if (input instanceof Uint8Array) return input;
171
+ if (typeof input === 'string') return new TextEncoder().encode(input);
172
+ return new TextEncoder().encode(JSON.stringify(input) || '');
173
+ }
174
+ });
175
+
176
+ export default Utils;