ths-csprng 1.0.2 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.nomedia ADDED
File without changes
package/README.md CHANGED
@@ -1,14 +1,60 @@
1
- # THS (Temporal-Hardening Solution)
1
+ <div align="center">
2
+ <img src="https://raw.githubusercontent.com/harnuma9/temporal-hardening-solution-csprng/refs/heads/main/media/banner.png" alt="Banner" width="1250">
3
+ </div>
2
4
 
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.
5
+ <br />
6
+
7
+ # ths-csprng
8
+
9
+ [![npm version](https://img.shields.io/npm/v/ths-csprng.svg)](https://www.npmjs.com/package/ths-csprng)
10
+ ![Node.js](https://img.shields.io/badge/Node.js-%3E%3D20-green)
11
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/harnuma9/temporal-hardening-solution-csprng/blob/main/LICENSE)
12
+ ![Verify Status](https://github.com/harnuma9/temporal-hardening-solution-csprng/actions/workflows/verify.yml/badge.svg)
13
+ [![Socket Badge](https://badge.socket.dev/npm/package/ths-csprng/1.1.1)](https://badge.socket.dev/npm/package/ths-csprng/1.1.1)
14
+ [![Donate](https://img.shields.io/badge/@Harnuma9-Donate-FF4D4D)](https://harnuma9.github.io/donate/)
15
+
16
+ <br />
17
+
18
+ Standard RNGs assume that once you’ve seeded them, the whole history of how that seed was created just disappears. In reality, if someone can reconstruct the exact physical state of your system—or somehow “look back” in time—your randomness stops being random.
19
+
20
+ If you buy into a **block universe** or **[superdeterminism](https://en.wikipedia.org/wiki/Superdeterminism)**, that “random” seed was never random at all. It’s just a fixed point in spacetime. To an observer with enough computing power or a view further down the timeline, your private key isn’t secret — it’s already history.
21
+
22
+ **THS (Temporal-Hardening Solution)** is an attempt to push back against that. It’s a cryptographic wrapper that turns the problem from “break the math” into “do a massive amount of physical work.” It makes retrocausal attacks or precise state reconstruction much harder by forcing the attacker to simulate real-world chaos.
23
+
24
+ In short: it uses macroscopic messiness as a defense against microscopic observation.
25
+
26
+ <br />
27
+
28
+ ---
29
+
30
+ <br />
31
+
32
+ <div align="center">
33
+ <img src="https://raw.githubusercontent.com/harnuma9/temporal-hardening-solution-csprng/refs/heads/main/media/illustration_1.png" alt="Illustration 1" width="1250">
34
+ </div>
35
+
36
+ <br />
37
+
38
+ ## The “Observer from the Future” Problem
39
+
40
+ 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.
41
+
42
+ Normally, a seed is generated at a single point in time (*T*<sub>0</sub>). If that moment is reconstructed, your entropy is gone.
43
+
44
+ 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.
45
+
46
+ <br />
47
+
48
+ > **Full API context optimized for AI assistants and contributors** is available in [`llms-full.txt`](https://github.com/harnuma9/temporal-hardening-solution-csprng/blob/main/llms-full.txt).
4
49
 
50
+ <br />
5
51
  <br />
6
52
 
7
53
  ---
8
54
 
9
55
  <br />
10
56
 
11
- ### 🚀 Quick Start
57
+ ### 🚀 Getting Started
12
58
 
13
59
  ```bash
14
60
  npm install ths-csprng
@@ -16,7 +62,9 @@ npm install ths-csprng
16
62
 
17
63
  <br />
18
64
 
19
- #### ESM (Node.js 18+, Vite, Bun)
65
+ ### 🛠️ Quick Start
66
+
67
+ #### Modern ESM (Node.js 18+, Vite, Bun)
20
68
 
21
69
  ```javascript
22
70
  import THS from 'ths-csprng';
@@ -24,18 +72,19 @@ import THS from 'ths-csprng';
24
72
  // Standard 256-bit secure random buffer
25
73
  const entropy = await THS.random(32);
26
74
 
27
- // High-security hardened key (Computational work required to reconstruct)
75
+ // High-security hardened key
28
76
  const masterKey = await THS.random(64, {
29
77
  layers: 512, // 512 temporal slices
30
78
  harden: 3, // Heavy Argon2id hardening
31
- mem: 65536 // 64MB memory cost
79
+ mem: 65536, // 64MB memory cost
80
+ trng: true // Try reading directly from /dev/urandom
32
81
  });
33
82
  ```
34
83
 
35
- #### CommonJS
84
+ #### Legacy / Standard CommonJS
36
85
 
37
86
  ```javascript
38
- const THS = require('ths-csprng');
87
+ const { THS } = require('ths-csprng');
39
88
 
40
89
  (async () => {
41
90
  const entropy = await THS.random(32);
@@ -47,7 +96,9 @@ const THS = require('ths-csprng');
47
96
 
48
97
  ---
49
98
 
50
- ### 💎 Hardening Levels
99
+ ### 💎 The Hardening Levels
100
+
101
+ You can dial the security up or down depending on how much “weight” you want the key to have.
51
102
 
52
103
  | Level | Mode | Description |
53
104
  | :--- | :--- | :--- |
@@ -60,20 +111,25 @@ const THS = require('ths-csprng');
60
111
 
61
112
  ---
62
113
 
63
- ### ⚙️ Full Configuration
114
+ ### ⚙️ Full Configuration Options
64
115
 
65
116
  ```javascript
66
- const bytes = await THS.random(32, {
117
+ const options = {
67
118
  layers: 128, // Temporal slices (2 to 65536)
68
119
  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
- });
120
+ trng: false, // Force /dev/urandom usage (w/ fallback)
121
+ buffer: true, // Return Node.js Buffer (false for Uint8Array)
122
+
123
+ // Argon2id Specifics (Requires harden: 3)
124
+ memoryH: 1, // Number of Argon2id cycles per layer
125
+ mem: 16384, // Memory cost in KB (default 16MB)
126
+ passes: 3, // (t) iterations
127
+ parallelism: 4, // (p) parallel threads
128
+
129
+ label: 'THS-Default-v1' // KMAC personalization string
130
+ };
131
+
132
+ const bytes = await THS.random(32, options);
77
133
  ```
78
134
 
79
135
  <br />
@@ -82,13 +138,15 @@ const bytes = await THS.random(32, {
82
138
 
83
139
  ### 🧬 Advanced API
84
140
 
141
+ <br />
142
+
85
143
  #### Direct Utils
86
144
 
87
145
  ```javascript
88
146
  // ESM
89
147
  import Utils from 'ths-csprng/utils';
90
- // CJS
91
- const Utils = require('ths-csprng/utils');
148
+ // or CJS
149
+ const { Utils } = require('ths-csprng/utils');
92
150
 
93
151
  // Get a raw metabolic snapshot (*T=n*)
94
152
  const snap = await Utils.snapshot(2);
@@ -101,41 +159,153 @@ const raw = await THS.raw(32);
101
159
  #### Browser Support
102
160
 
103
161
  ```javascript
104
- const buf = new Uint8Array(32);
105
- THS.fillRandom(buf);
162
+ // Uses window.crypto or node:crypto depending on environment
163
+ const buf1 = new Uint32Array(32);
164
+ const buf2 = new Uint16Array(32);
165
+ const buf3 = new Uint8Array(32);
166
+
167
+ THS.fillRandom(buf1);
168
+ THS.fillRandom(buf2);
169
+ THS.fillRandom(buf3);
106
170
  ```
107
171
 
108
172
 
109
173
  #### Convenient Aliases
110
174
 
111
175
  ```javascript
112
- import { randomBytes } from 'ths-csprng';
176
+ import { randomBytes, THS } from 'ths-csprng';
177
+
178
+ // Sync: Standard CSPRNG fill
179
+ const uintBytes = randomBytes(32); // Uint8Array
180
+ const bufferBytes = Buffer.from(randomBytes(32)); // Buffer
113
181
 
114
- await randomBytes(32);
182
+ // Async: Uses THS.random() temporal hardening
183
+ const secureBytes = await randomBytes(
184
+ 32,
185
+ true, // isHardenRNG flag
186
+ { layers: 64, harden: 2 }
187
+ );
188
+
189
+ // Method Aliases
115
190
  await THS.rand(32);
116
191
  await THS.rnd(32);
117
192
  ```
118
193
 
119
194
  <br />
120
195
 
196
+ ## Why should you care?
197
+
198
+ 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.
199
+
200
+ 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.
201
+
202
+ <br />
203
+
204
+ <div align="center">
205
+ <img src="https://raw.githubusercontent.com/harnuma9/temporal-hardening-solution-csprng/refs/heads/main/media/illustration_2.png" alt="Illustration 2" width="1250">
206
+ </div>
207
+
208
+ <br />
209
+
210
+ ## Use Cases
211
+
212
+ * **Everyday apps:** Use `THS.fillRandom(arr)` or `randomBytes(len)` for **fast, reliable, standard randomness**.
213
+ * **Entropy Research / Custom PRNGs:** Import the subpath `ths-csprng/utils` to feed metabolic snapshots directly into your own DRBGs as a source of high-variance, hardware-bound entropy.
214
+ * **High-value keys:** Use `THS.random()` with `harden: 3` when you want seeds that are computationally irreducible — an attacker would need to reconstruct exact nanosecond-level CPU and memory states across hundreds of noisy iterations.
215
+
216
+ <br />
217
+
218
+ ## Threat Model
219
+
220
+ Threat models range from the **hypothetical** ([retrocausality](https://www.nwo.nl/en/projects/hexwa91373) is possible in real-life + proofs) to the **theoretically niche** (such as the “[cold start](https://www.ijcrt.org/papers/IJCRT2602064.pdf)” entropy problem in serverless environments).
221
+
222
+ However, the most critical threats involve **real-world hardware vulnerabilities** that compromise the security of every cryptographic seed and encryption key generated on a device. Whether you are using post-quantum libraries or legacy suites, security is merely an illusion if:
223
+
224
+ * The entropy source is a single, static “snapshot” in time AND the adversary successfully recovers it.
225
+ * The source relies on a mathematically predictable DRBG source, such as the infamous **[DUAL_EC_DRBG](https://en.wikipedia.org/wiki/Dual_EC_DRBG)**.
226
+
227
+ <br />
228
+
229
+ ### ✅ Scope of Protection
230
+
231
+ The **THS-CSPRNG wrapper** is specifically engineered to mitigate scenarios where an adversary attempts to:
232
+
233
+ 1. **State Reconstruction**: Reconstruct the historical state of your machine at the precise moment a key was generated. (a core concern in [Cryptanalysis](https://en.wikipedia.org/wiki/Cryptanalysis)).
234
+ 2. **VM Replication**: Exploit **VM snapshots** or “cloning” events that cause multiple instances to produce identical, non-unique “random” output.
235
+ 3. **Entropy Subversion**: Bypass or predict hardware entropy sources via [manufacturer backdoors](https://en.wikipedia.org/wiki/Hardware_backdoor) or side-channel influence.
236
+
237
+ <br />
238
+
239
+ ### ❌ Limitations (What it can’t protect)
240
+
241
+ Although it is a CSPRNG wrapper, engineered to protect generated secrets from theoretical to real-world risks, it does not protect you from:
242
+
243
+ 1. **Actual time travelers**: They can travel from any time physically; that is cheating. A threat model specifically mitigates against any “observations” from 3D observers in the future using speculative “sci-fi” technologies.
244
+ 2. **Malware, Spyware, Exploits, etc.**: If your device is compromised at the OS level or pre-loaded with spyware, an adversary doesn't need to predict your entropy—they can simply **[intercept the final generated secrets](https://en.wikipedia.org/wiki/Form_grabbing)** from memory.
245
+ 3. **Physical Extraction**: If an adversary has physical access to your hardware and a soldering iron (or a very cold can of compressed air for a “[Cold Boot](https://en.wikipedia.org/wiki/Cold_boot_attack)” attack), they are bypassing the RNG logic entirely to target the stored result *(while TEEs and memory encryption help, physical access might be a game-over scenario)*.
246
+ 4. **Human errors**: High-quality entropy cannot save you from logic errors. Hardened seeds are useless if you accidentally log them to a public console, hardcode them into an automated script, or fall victim to a social engineering / **[MITM attack](https://en.wikipedia.org/wiki/Man-in-the-middle_attack)**.
247
+
248
+ <br />
249
+
250
+ > [!NOTE]
251
+ >
252
+ > **Hardening Level 3 (Heavy)** is computationally expensive by design. It is intended for generating long-term master keys or seeds.
253
+ > If your use case requires high-frequency random bytes (e.g., UUIDs or nonces), use **Level 0** or **Level 1**.
254
+
255
+ <br />
256
+
121
257
  ---
122
258
 
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.
259
+ <br />
128
260
 
129
- **Note:** Hardening Level 3 is computationally expensive. Use Level 0 or 1 for high-frequency needs like UUIDs or nonces.
261
+ ## 🛠 ️Development & Verification
262
+
263
+ ```bash
264
+ npm run check # test + verify checksums
265
+ npm run build # minify + generate docs + checksums
266
+ npm test
267
+ ```
268
+
269
+ <br />
270
+
271
+ The project uses:
272
+
273
+ * `terser` for minification
274
+ * SHA-256 checksums for all built files
275
+ * Strict mode + comprehensive test suite
276
+
277
+ <br />
278
+
279
+ ---
280
+
281
+ <br />
282
+
283
+ ## ⭐ Contributor(s)
284
+
285
+ <a href="https://github.com/harnuma9/temporal-hardening-solution-csprng/graphs/contributors">
286
+ <img alt="contributors" src="https://contrib.rocks/image?repo=harnuma9/temporal-hardening-solution-csprng"/>
287
+ </a>
130
288
 
289
+ <br />
290
+ <br />
131
291
  <br />
132
292
 
133
293
  ## License
134
294
 
135
295
  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.
136
296
 
137
- Copyright © 2026 Aries Harbinger. See the **[LICENSE](./LICENSE)** file for full details.
297
+ Copyright © 2026 Aries Harbinger. See the **[LICENSE](https://github.com/harnuma9/temporal-hardening-solution-csprng/blob/main/LICENSE)** file for full details.
138
298
 
299
+ <br />
300
+ <br />
301
+
302
+ ## 🔗 Links
303
+
304
+ * [Github Repository](https://github.com/harnuma9/temporal-hardening-solution-csprng)
305
+ * [npm Package](https://www.npmjs.com/package/ths-csprng)
306
+ * Full AI Context **[llms-full.txt](https://github.com/harnuma9/temporal-hardening-solution-csprng/blob/main/llms-full.txt)** (for contributors / LLM assistants)
307
+
308
+ <br />
139
309
  <br />
140
310
  <br />
141
311
  <br />
@@ -0,0 +1,2 @@
1
+ import Utils from"ths-csprng/utils";import{kmac256}from"@noble/hashes/sha3-addons.js";import{createHash,webcrypto}from"node:crypto";const THS=Object.freeze({async random(e=32,{layers:r=128,harden:t=2,trng:a=!1,buffer:o=!0,memoryH:n=1,mem:i=16384,passes:s=3,parallelism:l=4,label:c="THS-Default-v1"}={}){if(e<=0)return Buffer.alloc(0);if(r<2||r>65536)throw new Error(`Layer count [${r}] is invalid. Requires a temporal span of 2-65536 cycles to ensure metabolic variance.`);if(t<0||t>3)throw new Error(`Hardening level [${t}] is unknown. Use 0 (Basic) through 3 (Heavy).`);if(t>=3&&(n<1||n>r))throw new Error(`Memory hardening cycle count [${n}] exceeds available temporal layers [${r}].`);if("number"!=typeof i||i<1024)throw new Error(`Memory set [${i}] for Argon2id must be a valid number and not be less than 1024 or 1KB.`);const d=await Utils.getRandom(32,a);let m;try{m=kmac256.create(d,{personalization:Utils.toBytes(c),dkLen:e});for(let e=0;e<r;e++){const o=await Utils.snapshot(t,n,i,s,l,e,a);if(m.update(o),o.fill(0),t>=1){const e=await Utils.getRandom(12,a);m.update(e),e.fill(0)}e<r-1&&await Utils.jitter(a)}const f=m.digest();if(!o)return f;const w=Buffer.from(f);return f?.fill?.(0),w}catch(e){throw e}finally{d?.fill?.(0),m=null}},rand:async(e,r)=>await THS.random(e,r),rnd:async(e,r)=>await THS.random(e,r),raw:async e=>await Utils.getRandom(e,!0),fillRandom:e=>("undefined"!=typeof window&&window.crypto?window.crypto:webcrypto).getRandomValues(e)}),randomBytes=(e,r=!1,t)=>r?new Promise((r,a)=>THS.random(e,t).then(e=>r(e)).catch(e=>a(e))):THS.fillRandom(new Uint8Array(e));export{THS,randomBytes};export default THS;
2
+ //# sourceMappingURL=index.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dist/index.min.js.map","names":["Utils","kmac256","createHash","webcrypto","THS","Object","freeze","random","length","layers","harden","trng","buffer","memoryH","mem","passes","parallelism","label","Buffer","alloc","Error","seed","getRandom","k","create","personalization","toBytes","dkLen","i","moment","snapshot","update","fill","sup","jitter","out","digest","b","from","e","async","len","o","fillRandom","arr","window","crypto","getRandomValues","randomBytes","isHardenRNG","Promise","resolve","reject","then","catch","err","Uint8Array"],"sources":["src/index.js"],"mappings":"OAgBOA,UAAW,0BACTC,YAAe,sCACfC,WAAYC,cAAiB,cAsBtC,MAAMC,IAAMC,OAAOC,OAAO,CAYtB,YAAMC,CAAOC,EAAS,IAAIC,OAEtBA,EAAS,IAAGC,OACZA,EAAS,EAACC,KACVA,GAAO,EAAKC,OACZA,GAAS,EAAIC,QAEbA,EAAU,EAACC,IACXA,EAAM,MAAKC,OACXA,EAAS,EAACC,YACVA,EAAc,EAACC,MAEfA,EAAQ,kBAER,CAAC,GAED,GAAIT,GAAU,EACV,OAAOU,OAAOC,MAAM,GAExB,GAAIV,EAAS,GAAKA,EAAS,MACvB,MAAM,IAAIW,MAAM,gBAAgBX,2FAEpC,GAAIC,EAAS,GAAKA,EAAS,EACvB,MAAM,IAAIU,MAAM,oBAAoBV,mDAExC,GAAIA,GAAU,IAAMG,EAAU,GAAKA,EAAUJ,GACzC,MAAM,IAAIW,MAAM,iCAAiCP,yCAA+CJ,OAEpG,GAAI,iBAAoBK,GAAOA,EAAM,KACjC,MAAM,IAAIM,MAAM,eAAeN,4EAEnC,MAAMO,QAAarB,MAAMsB,UAAU,GAAIX,GACvC,IAAIY,EAEJ,IACIA,EAAItB,QAAQuB,OAAOH,EAAM,CACrBI,gBAAiBzB,MAAM0B,QAAQT,GAC/BU,MAAOnB,IAGX,IAAK,IAAIoB,EAAI,EAAGA,EAAInB,EAAQmB,IAAK,CAC7B,MAAMC,QAAe7B,MAAM8B,SAASpB,EAAQG,EAASC,EAAKC,EAAQC,EAAaY,EAAGjB,GAGlF,GAFAY,EAAEQ,OAAOF,GAASA,EAAOG,KAAK,GAE1BtB,GAAU,EAAG,CAEb,MAAMuB,QAAYjC,MAAMsB,UAAU,GAAIX,GACtCY,EAAEQ,OAAOE,GAAMA,EAAID,KAAK,EAC5B,CAEIJ,EAAInB,EAAS,SAAST,MAAMkC,OAAOvB,EAC3C,CAEA,MAAMwB,EAAMZ,EAAEa,SACd,IAAKxB,EAAQ,OAAOuB,EAEpB,MAAME,EAAInB,OAAOoB,KAAKH,GAEtB,OADAA,GAAKH,OAAO,GACLK,CACX,CAEA,MAAOE,GAAK,MAAMA,CAAG,CAErB,QACIlB,GAAMW,OAAO,GACbT,EAAI,IACR,CACJ,EAQAiB,KAAU,MAACC,EAAKC,UAAkBtC,IAAIG,OAAOkC,EAAKC,GAQlDF,IAAS,MAACC,EAAKC,UAAkBtC,IAAIG,OAAOkC,EAAKC,GAUjDF,IAAS,MAACC,SAAqBzC,MAAMsB,UAAUmB,GAAK,GASpDE,WAAWC,IAEgB,oBAAXC,QAA0BA,OAAOC,OACnCD,OAAOC,OACP3C,WACR4C,gBAAgBH,KAYpBI,YAAc,CAACP,EAAKQ,GAAc,EAAOP,IAEtCO,EAIE,IAAIC,QAAQ,CAACC,EAASC,IACzBhD,IAAIG,OAAOkC,EAAKC,GACXW,KAAKlB,GAAOgB,EAAQhB,IACpBmB,MAAMC,GAAOH,EAAOG,KANlBnD,IAAIuC,WAAW,IAAIa,WAAWf,WASpCrC,IAAK4C,4BACC5C","ignoreList":[]}
@@ -0,0 +1,2 @@
1
+ import fs from"node:fs/promises";import{promisify}from"node:util";import{randomBytes,timingSafeEqual,createHash,argon2 as _argon2}from"node:crypto";const argon2=promisify(_argon2),defined_past=process.hrtime.bigint();export const Utils=Object.freeze({async snapshot(e,t,n,r,i,a,o){if(e<=1)return this.bigIntToBuffer(process.hrtime.bigint());const s=process.resourceUsage(),f=process.memoryUsage(),g=[process.hrtime.bigint(),BigInt(Math.round(1e6*process.uptime())),BigInt(s.userCPUTime),BigInt(s.systemCPUTime),BigInt(s.minorPageFault),BigInt(s.majorPageFault),BigInt(s.voluntaryContextSwitches),BigInt(s.involuntaryContextSwitches),BigInt(f.heapUsed),BigInt(f.heapTotal),BigInt(f.external),BigInt(f.arrayBuffers||0),BigInt(f.rss),BigInt(Math.round(1e6*performance.now())),defined_past,BigInt(Date.now()),BigInt(a||0)].map(e=>this.bigIntToBuffer(e)),c=Buffer.allocUnsafe(128),l=createHash("sha3-512").update(c).update(process.hrtime.bigint().toString()).digest();c.fill(0);const u=Buffer.concat([l,...g]);if(l.fill(0),e<=2||a>=t)return u;let m,B;try{m=await this.getRandom(16,o),B=await argon2("argon2id",{nonce:m,message:u,memory:n,passes:r,parallelism:i,tagLength:64});const t=Buffer.concat([B,u]);if(e<=3)return t;throw new Error(`Hardening level [${e}] is unknown. Use 0 (Basic) through 3 (Heavy).`)}catch(e){throw e}finally{B?.fill?.(0),m?.fill?.(0),u?.fill?.(0)}},async getRandom(e,t){if(!t)return randomBytes(e);let n;try{n=await fs.open("/dev/urandom","r");const t=Buffer.allocUnsafe(e),{bytesRead:r}=await n.read(t,0,e,0);return r<e?randomBytes(e):t}catch(t){return randomBytes(e)}finally{n&&await n.close()}},async jitter(e){const t=await this.getRandom(64,e);timingSafeEqual(t,t),t.fill(0)},bigIntToBuffer(e){if("bigint"!=typeof e&&(e=BigInt(e)),e<0n)throw new RangeError("Negative values not supported");if(0n===e){const e=Buffer.allocUnsafe(3);return e.writeUInt16BE(1,0),e[2]=0,e}let t=e.toString(16);t.length%2!=0&&(t="0"+t);const n=Buffer.from(t,"hex");if(n.length>65535)throw new RangeError("BigInt exceeds 16-bit length header capacity");const r=Buffer.allocUnsafe(2);return r.writeUInt16BE(n.length,0),Buffer.concat([r,n])},toBytes:e=>e instanceof Uint8Array?e:"string"==typeof e?(new TextEncoder).encode(e):(new TextEncoder).encode(JSON.stringify(e)||"")});export default Utils;
2
+ //# sourceMappingURL=util.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dist/util.min.js.map","names":["fs","promisify","randomBytes","timingSafeEqual","createHash","_argon2","argon2","defined_past","process","hrtime","bigint","Utils","Object","freeze","snapshot","h","mh","mem","t","p","s","useUrandom","this","bigIntToBuffer","u","resourceUsage","m","memoryUsage","metrics","BigInt","Math","round","uptime","userCPUTime","systemCPUTime","minorPageFault","majorPageFault","voluntaryContextSwitches","involuntaryContextSwitches","heapUsed","heapTotal","external","arrayBuffers","rss","performance","now","Date","map","n","noise","Buffer","allocUnsafe","hashedNoise","update","toString","digest","fill","b","concat","nonce","memHard","getRandom","message","memory","passes","parallelism","tagLength","result","Error","e","len","fd","open","buffer","bytesRead","read","close","jitter","RangeError","buf","writeUInt16BE","hex","length","data","from","header","toBytes","input","Uint8Array","TextEncoder","encode","JSON","stringify"],"sources":["src/util.js"],"mappings":"OAgBOA,OAAQ,0BACNC,cAAiB,mBACjBC,YAAaC,gBAAiBC,qBAAsBC,YAAe,cAE5E,MAAMC,OAASL,UAAUI,SACnBE,aAAeC,QAAQC,OAAOC,gBAQ7B,MAAMC,MAAQC,OAAOC,OAAO,CAiB/B,cAAMC,CAASC,EAAGC,EAAIC,EAAKC,EAAGC,EAAGC,EAAGC,GAEhC,GAAIN,GAAK,EAAG,OAAOO,KAAKC,eAAef,QAAQC,OAAOC,UAEtD,MAAMc,EAAIhB,QAAQiB,gBACZC,EAAIlB,QAAQmB,cAGZC,EAAU,CACZpB,QAAQC,OAAOC,SACfmB,OAAOC,KAAKC,MAAyB,IAAnBvB,QAAQwB,WAC1BH,OAAOL,EAAES,aACTJ,OAAOL,EAAEU,eACTL,OAAOL,EAAEW,gBACTN,OAAOL,EAAEY,gBACTP,OAAOL,EAAEa,0BACTR,OAAOL,EAAEc,4BACTT,OAAOH,EAAEa,UACTV,OAAOH,EAAEc,WACTX,OAAOH,EAAEe,UACTZ,OAAOH,EAAEgB,cAAgB,GACzBb,OAAOH,EAAEiB,KACTd,OAAOC,KAAKC,MAA0B,IAApBa,YAAYC,QAC9BtC,aACAsB,OAAOiB,KAAKD,OACZhB,OAAOT,GAAK,IACd2B,IAAIC,GAAK1B,KAAKC,eAAeyB,IAIzBC,EAAQC,OAAOC,YAAY,KAG3BC,EAAchD,WAAW,YAC1BiD,OAAOJ,GACPI,OAAO7C,QAAQC,OAAOC,SAAS4C,YAC/BC,SAGLN,EAAMO,KAAK,GAGX,MAAMC,EAAIP,OAAOQ,OAAO,CACpBN,KACGxB,IAIP,GAFAwB,EAAYI,KAAK,GAEbzC,GAAK,GAAKK,GAAKJ,EAAI,OAAOyC,EAG9B,IAAIE,EAAOC,EAEX,IACID,QAAgBrC,KAAKuC,UAAU,GAAIxC,GACnCuC,QAAgBtD,OAAO,WAAY,CAC/BqD,QACAG,QAASL,EACTM,OAAQ9C,EACR+C,OAAQ9C,EACR+C,YAAa9C,EACb+C,UAAW,KAGf,MAAMC,EAASjB,OAAOQ,OAAO,CAACE,EAASH,IACvC,GAAI1C,GAAK,EAAG,OAAOoD,EAEnB,MAAM,IAAIC,MAAM,oBAAoBrD,kDACxC,CAEA,MAAOsD,GAAK,MAAMA,CAAG,CAErB,QAEIT,GAASJ,OAAO,GAChBG,GAAOH,OAAO,GACdC,GAAGD,OAAO,EACd,CACJ,EAaA,eAAMK,CAAUS,EAAKjD,GACjB,IAAKA,EAAY,OAAOnB,YAAYoE,GAEpC,IAAIC,EACJ,IACIA,QAAWvE,GAAGwE,KAAK,eAAgB,KACnC,MAAMC,EAASvB,OAAOC,YAAYmB,IAC5BI,UAAEA,SAAoBH,EAAGI,KAAKF,EAAQ,EAAGH,EAAK,GAGpD,OAAQI,EAAYJ,EACdpE,YAAYoE,GACZG,CACV,CAEA,MAAOJ,GAAK,OAAOnE,YAAYoE,EAAO,CACtC,QAAgBC,SAAUA,EAAGK,OAAS,CAC1C,EAWA,YAAMC,CAAOxD,GACT,MAAMoC,QAAUnC,KAAKuC,UAAU,GAAIxC,GACnClB,gBAAgBsD,EAAGA,GACnBA,EAAED,KAAK,EACX,EAWA,cAAAjC,CAAeyB,GAEX,GADiB,iBAANA,IAAgBA,EAAInB,OAAOmB,IAClCA,EAAI,GAAI,MAAM,IAAI8B,WAAW,iCAGjC,GAAU,KAAN9B,EAAU,CACV,MAAM+B,EAAM7B,OAAOC,YAAY,GAG/B,OAFA4B,EAAIC,cAAc,EAAG,GACrBD,EAAI,GAAK,EACFA,CACX,CAGA,IAAIE,EAAMjC,EAAEM,SAAS,IACjB2B,EAAIC,OAAS,GAAM,IAAGD,EAAM,IAAMA,GACtC,MAAME,EAAOjC,OAAOkC,KAAKH,EAAK,OAG9B,GAAIE,EAAKD,OAAS,MACd,MAAM,IAAIJ,WAAW,gDAGzB,MAAMO,EAASnC,OAAOC,YAAY,GAGlC,OAFAkC,EAAOL,cAAcG,EAAKD,OAAQ,GAE3BhC,OAAOQ,OAAO,CAAC2B,EAAQF,GAClC,EAUAG,QAAQC,GACAA,aAAiBC,WAAmBD,EACnB,iBAAVA,GAA2B,IAAIE,aAAcC,OAAOH,IACxD,IAAIE,aAAcC,OAAOC,KAAKC,UAAUL,IAAU,qBAIlD5E","ignoreList":[]}
package/package.json CHANGED
@@ -1,36 +1,46 @@
1
1
  {
2
2
  "name": "ths-csprng",
3
- "version": "1.0.2",
3
+ "version": "1.1.1",
4
4
  "description": "Temporal-Hardening Solution: A CSPRNG wrapper designed to resist historical state reconstruction and hardware backdoors.",
5
- "main": "./src/index.js",
6
- "files": [
7
- "src",
8
- "LICENSE",
9
- "README.md",
10
- "npm-shrinkwrap.json"
11
- ],
12
- "exports": {
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
- }
21
- },
22
5
  "author": "Aries Harbinger",
23
6
  "license": "Apache-2.0",
24
- "homepage": "https://github.com/harnuma9/temporal-hardening-solution-csprng",
7
+ "homepage": "https://github.com/harnuma9/temporal-hardening-solution-csprng#readme",
25
8
  "repository": {
26
9
  "type": "git",
27
10
  "url": "git+https://github.com/harnuma9/temporal-hardening-solution-csprng.git"
28
11
  },
12
+ "funding": [
13
+ {
14
+ "type": "individual",
15
+ "url": "https://harnuma9.github.io/donate/"
16
+ }
17
+ ],
29
18
  "bugs": {
30
19
  "url": "https://github.com/harnuma9/temporal-hardening-solution-csprng/issues"
31
20
  },
21
+ "type": "module",
22
+ "types": "./src/index.d.ts",
23
+ "exports": {
24
+ ".": {
25
+ "types": "./src/index.d.ts",
26
+ "import": "./dist/index.min.js",
27
+ "require": "./dist/index.min.js"
28
+ },
29
+ "./utils": {
30
+ "types": "./src/util.d.ts",
31
+ "import": "./dist/util.min.js",
32
+ "require": "./dist/util.min.js"
33
+ }
34
+ },
32
35
  "engines": {
33
- "node": ">=18"
36
+ "node": ">=20"
37
+ },
38
+ "scripts": {
39
+ "build": "bash update.sh",
40
+ "verify": "bash verify.sh",
41
+ "test": "node --test ./test/test_suite.js && node --test ./test/benchmark.js",
42
+ "check": "npm run test && npm run verify",
43
+ "prepublishOnly": "npm run check"
34
44
  },
35
45
  "keywords": [
36
46
  "csprng",
@@ -47,6 +57,10 @@
47
57
  "dependencies": {
48
58
  "@noble/hashes": "^2.2.0"
49
59
  },
60
+ "devDependencies": {
61
+ "@types/node": "^25.6.0",
62
+ "docdash": "^2.0.2"
63
+ },
50
64
  "publishConfig": {
51
65
  "access": "public"
52
66
  }
package/src/index.d.ts ADDED
@@ -0,0 +1,108 @@
1
+ export default THS;
2
+ /**
3
+ * Configuration options for temporal hardening.
4
+ */
5
+ export type THSOptions = {
6
+ /**
7
+ * - Number of timeline slices (iterations). Range: 2-65536.
8
+ */
9
+ layers?: number | undefined;
10
+ /**
11
+ * - Hardening level: 0 (hrtime), 1 (+entropy), 2 (+internal), 3 (+Argon2id).
12
+ */
13
+ harden?: number | undefined;
14
+ /**
15
+ * - Attempts to read from /dev/urandom; falls back to CSPRNG.
16
+ */
17
+ trng?: boolean | undefined;
18
+ /**
19
+ * - If true, returns a Node.js Buffer; otherwise, returns a Uint8Array.
20
+ */
21
+ buffer?: boolean | undefined;
22
+ /**
23
+ * - Number of Argon2id runs (Requires harden level 3).
24
+ */
25
+ memoryH?: number | undefined;
26
+ /**
27
+ * - Memory hardness for Argon2id in KiB (Default: 16MB).
28
+ */
29
+ mem?: number | undefined;
30
+ /**
31
+ * - Number of iterations (t) for Argon2id.
32
+ */
33
+ passes?: number | undefined;
34
+ /**
35
+ * - Degree of parallelism (p) for Argon2id.
36
+ */
37
+ parallelism?: number | undefined;
38
+ /**
39
+ * - KMAC personalization string.
40
+ */
41
+ label?: string | undefined;
42
+ };
43
+ /**
44
+ * Configuration options for temporal hardening.
45
+ * @typedef {Object} THSOptions
46
+ * @property {number} [layers=128] - Number of timeline slices (iterations). Range: 2-65536.
47
+ * @property {number} [harden=2] - Hardening level: 0 (hrtime), 1 (+entropy), 2 (+internal), 3 (+Argon2id).
48
+ * @property {boolean} [trng=false] - Attempts to read from /dev/urandom; falls back to CSPRNG.
49
+ * @property {boolean} [buffer=true] - If true, returns a Node.js Buffer; otherwise, returns a Uint8Array.
50
+ * @property {number} [memoryH=1] - Number of Argon2id runs (Requires harden level 3).
51
+ * @property {number} [mem=16384] - Memory hardness for Argon2id in KiB (Default: 16MB).
52
+ * @property {number} [passes=3] - Number of iterations (t) for Argon2id.
53
+ * @property {number} [parallelism=4] - Degree of parallelism (p) for Argon2id.
54
+ * @property {string} [label='THS-Default-v1'] - KMAC personalization string.
55
+ */
56
+ /**
57
+ * Temporal-Hardening Solution - Applied in Random Number Generator.
58
+ * @namespace THS
59
+ * @readonly
60
+ */
61
+ export const THS: Readonly<{
62
+ /**
63
+ * Generates cryptographically secure random bytes with temporal hardening.
64
+ * Incorporates time-variance and system entropy over multiple metabolic cycles.
65
+ * @async
66
+ * @memberof THS
67
+ * @param {number} [length=32] - The desired length of the output bytes.
68
+ * @param {THSOptions} [options={}] - Hardening and hashing configuration.
69
+ * @returns {Promise<Buffer|Uint8Array>} The hardened random bytes.
70
+ * @throws {Error} If layers, harden level, or memory parameters are out of bounds.
71
+ */
72
+ random(length?: number, { layers, harden, trng, buffer, memoryH, mem, passes, parallelism, label }?: THSOptions): Promise<Buffer | Uint8Array>;
73
+ /**
74
+ * Alias for {@link THS.random}
75
+ * @async
76
+ * @memberof THS
77
+ */
78
+ rand(len: any, o: any): Promise<any>;
79
+ /**
80
+ * Alias for {@link THS.random}
81
+ * @async
82
+ * @memberof THS
83
+ */
84
+ rnd(len: any, o: any): Promise<any>;
85
+ /**
86
+ * Direct access to entropy sources (e.g., /dev/urandom) without temporal hardening.
87
+ * @async
88
+ * @memberof THS
89
+ * @param {number} len - Byte length to retrieve.
90
+ * @returns {Promise<Buffer>}
91
+ */
92
+ raw(len: number): Promise<Buffer>;
93
+ /**
94
+ * Fills a typed array with random values using the environment's Webcrypto implementation.
95
+ * @memberof THS
96
+ * @param {Uint8Array|Int8Array|Uint16Array|Int16Array|Uint32Array|Int32Array} arr - The array to fill.
97
+ * @returns {Uint8Array|Int8Array|Uint16Array|Int16Array|Uint32Array|Int32Array} The filled array.
98
+ */
99
+ fillRandom(arr: Uint8Array | Int8Array | Uint16Array | Int16Array | Uint32Array | Int32Array): Uint8Array | Int8Array | Uint16Array | Int16Array | Uint32Array | Int32Array;
100
+ }>;
101
+ /**
102
+ * Universal wrapper for random byte generation.
103
+ * @param {number} len - The number of bytes to generate.
104
+ * @param {boolean} [isHardenRNG=false] - If true, uses the asynchronous THS hardening logic.
105
+ * @param {THSOptions} [o] - Options passed only if isHardenRNG is true.
106
+ * @returns {Uint8Array|Promise<Buffer|Uint8Array>} Returns Uint8Array if synchronous, otherwise a Promise.
107
+ */
108
+ export function randomBytes(len: number, isHardenRNG?: boolean, o?: THSOptions): Uint8Array | Promise<Buffer | Uint8Array>;
package/src/index.js CHANGED
@@ -1,6 +1,4 @@
1
1
  /**
2
- * THS (Temporal-Hardening Solution)
3
- *
4
2
  * Copyright 2026 Aries Harbinger
5
3
  *
6
4
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,17 +14,41 @@
16
14
  * limitations under the License.
17
15
  */
18
16
 
19
- const Utils = require('./util.js');
20
- const { kmac256 } = require('@noble/hashes/sha3-addons.js');
21
- const { webcrypto } = require('node:crypto');
17
+ import Utils from 'ths-csprng/utils';
18
+ import { kmac256 } from '@noble/hashes/sha3-addons.js';
19
+ import { createHash, webcrypto } from 'node:crypto';
20
+
22
21
 
22
+ /**
23
+ * Configuration options for temporal hardening.
24
+ * @typedef {Object} THSOptions
25
+ * @property {number} [layers=128] - Number of timeline slices (iterations). Range: 2-65536.
26
+ * @property {number} [harden=2] - Hardening level: 0 (hrtime), 1 (+entropy), 2 (+internal), 3 (+Argon2id).
27
+ * @property {boolean} [trng=false] - Attempts to read from /dev/urandom; falls back to CSPRNG.
28
+ * @property {boolean} [buffer=true] - If true, returns a Node.js Buffer; otherwise, returns a Uint8Array.
29
+ * @property {number} [memoryH=1] - Number of Argon2id runs (Requires harden level 3).
30
+ * @property {number} [mem=16384] - Memory hardness for Argon2id in KiB (Default: 16MB).
31
+ * @property {number} [passes=3] - Number of iterations (t) for Argon2id.
32
+ * @property {number} [parallelism=4] - Degree of parallelism (p) for Argon2id.
33
+ * @property {string} [label='THS-Default-v1'] - KMAC personalization string.
34
+ */
23
35
 
24
36
  /**
25
- * Temporal-Hardening Solution - Applied in Random Number Generator
37
+ * Temporal-Hardening Solution - Applied in Random Number Generator.
38
+ * @namespace THS
39
+ * @readonly
26
40
  */
27
41
  const THS = Object.freeze({
42
+
28
43
  /**
29
44
  * Generates cryptographically secure random bytes with temporal hardening.
45
+ * Incorporates time-variance and system entropy over multiple metabolic cycles.
46
+ * @async
47
+ * @memberof THS
48
+ * @param {number} [length=32] - The desired length of the output bytes.
49
+ * @param {THSOptions} [options={}] - Hardening and hashing configuration.
50
+ * @returns {Promise<Buffer|Uint8Array>} The hardened random bytes.
51
+ * @throws {Error} If layers, harden level, or memory parameters are out of bounds.
30
52
  */
31
53
  async random(length = 32, {
32
54
 
@@ -39,6 +61,7 @@ const THS = Object.freeze({
39
61
  mem = 16384, // memory hardness of Argon2id (default: 16MB)
40
62
  passes = 3, // (t) passes for Argon2id (default: 3)
41
63
  parallelism = 4, // (p) parallelism for Argon2id (default: 4)
64
+
42
65
  label = 'THS-Default-v1' // kmac label
43
66
 
44
67
  } = {}) {
@@ -84,24 +107,51 @@ const THS = Object.freeze({
84
107
  if (!buffer) return out;
85
108
 
86
109
  const b = Buffer.from(out);
87
- return out.fill(0), b;
110
+ out?.fill?.(0);
111
+ return b;
88
112
  }
89
113
 
90
114
  catch (e) { throw e; }
91
115
 
92
116
  finally {
93
- seed.fill(0);
117
+ seed?.fill?.(0);
94
118
  k = null;
95
119
  }
96
120
  },
97
121
 
98
- rand(len, o) { return THS.random(len, o) },
99
- rnd (len, o) { return THS.random(len, o) },
100
122
 
101
- // Direct use of /dev/urandom (fallback to randomBytes)
123
+ /**
124
+ * Alias for {@link THS.random}
125
+ * @async
126
+ * @memberof THS
127
+ */
128
+ async rand(len, o) { return await THS.random(len, o) },
129
+
130
+
131
+ /**
132
+ * Alias for {@link THS.random}
133
+ * @async
134
+ * @memberof THS
135
+ */
136
+ async rnd(len, o) { return await THS.random(len, o) },
137
+
138
+
139
+ /**
140
+ * Direct access to entropy sources (e.g., /dev/urandom) without temporal hardening.
141
+ * @async
142
+ * @memberof THS
143
+ * @param {number} len - Byte length to retrieve.
144
+ * @returns {Promise<Buffer>}
145
+ */
102
146
  async raw(len) { return await Utils.getRandom(len, true) },
103
147
 
104
- // Direct Webcrypto access
148
+
149
+ /**
150
+ * Fills a typed array with random values using the environment's Webcrypto implementation.
151
+ * @memberof THS
152
+ * @param {Uint8Array|Int8Array|Uint16Array|Int16Array|Uint32Array|Int32Array} arr - The array to fill.
153
+ * @returns {Uint8Array|Int8Array|Uint16Array|Int16Array|Uint32Array|Int32Array} The filled array.
154
+ */
105
155
  fillRandom(arr) {
106
156
  return (
107
157
  (typeof window !== 'undefined' && window.crypto)
@@ -111,6 +161,25 @@ const THS = Object.freeze({
111
161
  }
112
162
  });
113
163
 
114
- module.exports = THS;
115
- module.exports.THS = THS;
116
- module.exports.randomBytes = (len, o) => THS.random(len, o);
164
+
165
+ /**
166
+ * Universal wrapper for random byte generation.
167
+ * @param {number} len - The number of bytes to generate.
168
+ * @param {boolean} [isHardenRNG=false] - If true, uses the asynchronous THS hardening logic.
169
+ * @param {THSOptions} [o] - Options passed only if isHardenRNG is true.
170
+ * @returns {Uint8Array|Promise<Buffer|Uint8Array>} Returns Uint8Array if synchronous, otherwise a Promise.
171
+ */
172
+ const randomBytes = (len, isHardenRNG = false, o) => {
173
+ // Simple Random (Synchronous)
174
+ if (!isHardenRNG)
175
+ return THS.fillRandom(new Uint8Array(len));
176
+
177
+ // Hardened Random (Asynchronous)
178
+ return new Promise((resolve, reject) =>
179
+ THS.random(len, o)
180
+ .then(out => resolve(out))
181
+ .catch(err => reject(err)));
182
+ };
183
+
184
+ export { THS, randomBytes };
185
+ export default THS;
package/src/util.d.ts ADDED
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @namespace Utils
3
+ * @description A collection of cryptographic and system-entropy utilities for the THS framework.
4
+ * @readonly
5
+ */
6
+ export const Utils: Readonly<{
7
+ /**
8
+ * Captures a high-resolution snapshot of the Node.js metabolic state.
9
+ * Uses concatenation to prevent entropy collisions and optionally applies Argon2id hardening.
10
+ * @async
11
+ * @memberof Utils
12
+ * @param {number} h - Hardening level: 1 (Basic), 2 (Machine State), 3 (Argon2id).
13
+ * @param {number} mh - Maximum hardening threshold for the sequential salt.
14
+ * @param {number} mem - Memory cost for Argon2id (in KiB).
15
+ * @param {number} t - Iterations (time cost) for Argon2id.
16
+ * @param {number} p - Parallelism factor for Argon2id.
17
+ * @param {number} s - Sequential salt/counter to prevent state collisions.
18
+ * @param {boolean} useUrandom - If true, attempts to read from /dev/urandom directly.
19
+ * @returns {Promise<Buffer>} A buffer containing the metabolic snapshot.
20
+ * @throws {Error} Throws if an unknown hardening level is provided or if Argon2 fails.
21
+ */
22
+ snapshot(h: number, mh: number, mem: number, t: number, p: number, s: number, useUrandom: boolean): Promise<Buffer>;
23
+ /**
24
+ * Attempts to harvest raw entropy from the OS device (/dev/urandom).
25
+ * Fallbacks to hardware-backed Node.js `crypto.randomBytes` on failure or non-linux systems.
26
+ *
27
+ * @async
28
+ * @memberof Utils
29
+ * @param {number} len - The number of bytes to generate.
30
+ * @param {boolean} useUrandom - Whether to force usage of /dev/urandom.
31
+ * @returns {Promise<Buffer>} A buffer containing random bytes.
32
+ */
33
+ getRandom(len: number, useUrandom: boolean): Promise<Buffer>;
34
+ /**
35
+ * Micro-architectural noise generator.
36
+ * Performs a timing-safe comparison on random data to create CPU jitter and then wipes the buffer.
37
+ * @async
38
+ * @memberof Utils
39
+ * @param {boolean} useUrandom - Whether to use /dev/urandom for the jitter source.
40
+ * @returns {Promise<void>}
41
+ */
42
+ jitter(useUrandom: boolean): Promise<void>;
43
+ /**
44
+ * Optimized BigInt to Buffer conversion with a 16-bit Length Header.
45
+ * Prepends a 2-byte length header (Big-Endian) followed by the big-endian representation of the number.
46
+ * @memberof Utils
47
+ * @param {bigint|number} n - The BigInt or number to convert.
48
+ * @returns {Buffer} Buffer formatted as `[Length(2 bytes)][Data]`.
49
+ * @throws {RangeError} If the value is negative or the resulting byte length exceeds 65535.
50
+ */
51
+ bigIntToBuffer(n: bigint | number): Buffer;
52
+ /**
53
+ * Normalizes various input types (objects, strings, Uint8Arrays) into a Uint8Array.
54
+ * Useful for preparing data for cryptographic hashing.
55
+ * @memberof Utils
56
+ * @param {Uint8Array|string|object} input - The data to normalize.
57
+ * @returns {Uint8Array} The input represented as bytes.
58
+ */
59
+ toBytes(input: Uint8Array | string | object): Uint8Array;
60
+ }>;
61
+ export default Utils;
package/src/util.js CHANGED
@@ -1,6 +1,4 @@
1
1
  /**
2
- * THS (Temporal-Hardening Solution)
3
- *
4
2
  * Copyright 2026 Aries Harbinger
5
3
  *
6
4
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,18 +14,35 @@
16
14
  * limitations under the License.
17
15
  */
18
16
 
19
- const fs = require('node:fs/promises');
20
- const { promisify } = require('node:util');
21
- const { randomBytes, timingSafeEqual, argon2: _argon2 } = require('node:crypto');
17
+ import fs from 'node:fs/promises';
18
+ import { promisify } from 'node:util';
19
+ import { randomBytes, timingSafeEqual, createHash, argon2 as _argon2 } from 'node:crypto';
22
20
 
23
21
  const argon2 = promisify(_argon2);
24
22
  const defined_past = process.hrtime.bigint();
25
23
 
26
24
 
27
- const Utils = Object.freeze({
25
+ /**
26
+ * @namespace Utils
27
+ * @description A collection of cryptographic and system-entropy utilities for the THS framework.
28
+ * @readonly
29
+ */
30
+ export const Utils = Object.freeze({
31
+
28
32
  /**
29
33
  * Captures a high-resolution snapshot of the Node.js metabolic state.
30
- * Uses concatenation to prevent entropy collisions.
34
+ * Uses concatenation to prevent entropy collisions and optionally applies Argon2id hardening.
35
+ * @async
36
+ * @memberof Utils
37
+ * @param {number} h - Hardening level: 1 (Basic), 2 (Machine State), 3 (Argon2id).
38
+ * @param {number} mh - Maximum hardening threshold for the sequential salt.
39
+ * @param {number} mem - Memory cost for Argon2id (in KiB).
40
+ * @param {number} t - Iterations (time cost) for Argon2id.
41
+ * @param {number} p - Parallelism factor for Argon2id.
42
+ * @param {number} s - Sequential salt/counter to prevent state collisions.
43
+ * @param {boolean} useUrandom - If true, attempts to read from /dev/urandom directly.
44
+ * @returns {Promise<Buffer>} A buffer containing the metabolic snapshot.
45
+ * @throws {Error} Throws if an unknown hardening level is provided or if Argon2 fails.
31
46
  */
32
47
  async snapshot(h, mh, mem, t, p, s, useUrandom) {
33
48
  // Level 1: Minimal data
@@ -57,15 +72,25 @@ const Utils = Object.freeze({
57
72
  BigInt(s || 0) // Sequential salt
58
73
  ].map(n => this.bigIntToBuffer(n));
59
74
 
60
- // Map metrics to uniquely structured buffers
61
- const b = Buffer.concat([
75
+ // 128 bytes of raw, uninitialized system memory
76
+ // (May contain fragments of previous internal function calls/pointers, which is an additional entropy source)
77
+ const noise = Buffer.allocUnsafe(128);
78
+
79
+ // Hash that insecure system memory with SHA3-512
80
+ const hashedNoise = createHash('sha3-512')
81
+ .update(noise)
82
+ .update(process.hrtime.bigint().toString())
83
+ .digest();
62
84
 
63
- // Inject 128 bytes of raw, uninitialized system memory
64
- // (May contain fragments of previous internal function calls/pointers)
65
- Buffer.allocUnsafe(128),
85
+ // Wipe the noise immediately
86
+ noise.fill(0);
66
87
 
88
+ // Map metrics to uniquely structured buffers
89
+ const b = Buffer.concat([
90
+ hashedNoise, // This is a COPY of the digest
67
91
  ...metrics
68
92
  ]);
93
+ hashedNoise.fill(0); // Wipe the hashed noise
69
94
 
70
95
  if (h <= 2 || s >= mh) return b;
71
96
 
@@ -93,15 +118,22 @@ const Utils = Object.freeze({
93
118
 
94
119
  finally {
95
120
  // wipe result
96
- if (memHard) memHard.fill(0);
97
- if (nonce) nonce.fill(0);
98
- b.fill(0);
121
+ memHard?.fill?.(0);
122
+ nonce?.fill?.(0);
123
+ b?.fill?.(0);
99
124
  }
100
125
  },
101
126
 
127
+
102
128
  /**
103
- * Attempts to harvest raw entropy from the OS device.
104
- * Fallback to hardware-backed Node.js randomBytes on failure.
129
+ * Attempts to harvest raw entropy from the OS device (/dev/urandom).
130
+ * Fallbacks to hardware-backed Node.js `crypto.randomBytes` on failure or non-linux systems.
131
+ *
132
+ * @async
133
+ * @memberof Utils
134
+ * @param {number} len - The number of bytes to generate.
135
+ * @param {boolean} useUrandom - Whether to force usage of /dev/urandom.
136
+ * @returns {Promise<Buffer>} A buffer containing random bytes.
105
137
  */
106
138
  async getRandom(len, useUrandom) {
107
139
  if (!useUrandom) return randomBytes(len);
@@ -122,8 +154,14 @@ const Utils = Object.freeze({
122
154
  finally { if (fd) await fd.close(); }
123
155
  },
124
156
 
157
+
125
158
  /**
126
159
  * Micro-architectural noise generator.
160
+ * Performs a timing-safe comparison on random data to create CPU jitter and then wipes the buffer.
161
+ * @async
162
+ * @memberof Utils
163
+ * @param {boolean} useUrandom - Whether to use /dev/urandom for the jitter source.
164
+ * @returns {Promise<void>}
127
165
  */
128
166
  async jitter(useUrandom) {
129
167
  const b = await this.getRandom(64, useUrandom);
@@ -131,9 +169,14 @@ const Utils = Object.freeze({
131
169
  b.fill(0);
132
170
  },
133
171
 
172
+
134
173
  /**
135
- * Optimized BigInt to Buffer conversion with 16-bit Length Header.
136
- * Prepends a 2-byte length header (Big-Endian) followed by the data.
174
+ * Optimized BigInt to Buffer conversion with a 16-bit Length Header.
175
+ * Prepends a 2-byte length header (Big-Endian) followed by the big-endian representation of the number.
176
+ * @memberof Utils
177
+ * @param {bigint|number} n - The BigInt or number to convert.
178
+ * @returns {Buffer} Buffer formatted as `[Length(2 bytes)][Data]`.
179
+ * @throws {RangeError} If the value is negative or the resulting byte length exceeds 65535.
137
180
  */
138
181
  bigIntToBuffer(n) {
139
182
  if (typeof n !== 'bigint') n = BigInt(n);
@@ -163,8 +206,13 @@ const Utils = Object.freeze({
163
206
  return Buffer.concat([header, data]);
164
207
  },
165
208
 
209
+
166
210
  /**
167
- * Normalizes various input types into a Uint8Array.
211
+ * Normalizes various input types (objects, strings, Uint8Arrays) into a Uint8Array.
212
+ * Useful for preparing data for cryptographic hashing.
213
+ * @memberof Utils
214
+ * @param {Uint8Array|string|object} input - The data to normalize.
215
+ * @returns {Uint8Array} The input represented as bytes.
168
216
  */
169
217
  toBytes(input) {
170
218
  if (input instanceof Uint8Array) return input;
@@ -173,5 +221,4 @@ const Utils = Object.freeze({
173
221
  }
174
222
  });
175
223
 
176
- module.exports = Utils;
177
- module.exports.Utils = Utils;
224
+ export default Utils;
@@ -1,31 +0,0 @@
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/src/index.mjs DELETED
@@ -1,117 +0,0 @@
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.mjs DELETED
@@ -1,176 +0,0 @@
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;