ths-csprng 1.1.0 → 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,9 +1,20 @@
1
1
  <div align="center">
2
- <img src="./media/banner.png" alt="Banner" width="1250">
2
+ <img src="https://raw.githubusercontent.com/harnuma9/temporal-hardening-solution-csprng/refs/heads/main/media/banner.png" alt="Banner" width="1250">
3
3
  </div>
4
4
 
5
5
  <br />
6
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
+
7
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.
8
19
 
9
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.
@@ -19,7 +30,7 @@ In short: it uses macroscopic messiness as a defense against microscopic observa
19
30
  <br />
20
31
 
21
32
  <div align="center">
22
- <img src="./media/illustration_1.png" alt="Illustration 1" width="1250">
33
+ <img src="https://raw.githubusercontent.com/harnuma9/temporal-hardening-solution-csprng/refs/heads/main/media/illustration_1.png" alt="Illustration 1" width="1250">
23
34
  </div>
24
35
 
25
36
  <br />
@@ -34,6 +45,11 @@ THS fixes this by replacing a single seed with a temporal sequence of many momen
34
45
 
35
46
  <br />
36
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).
49
+
50
+ <br />
51
+ <br />
52
+
37
53
  ---
38
54
 
39
55
  <br />
@@ -68,7 +84,7 @@ const masterKey = await THS.random(64, {
68
84
  #### Legacy / Standard CommonJS
69
85
 
70
86
  ```javascript
71
- const THS = require('ths-csprng');
87
+ const { THS } = require('ths-csprng');
72
88
 
73
89
  (async () => {
74
90
  const entropy = await THS.random(32);
@@ -103,13 +119,13 @@ const options = {
103
119
  harden: 2, // 0–3 (hardening depth)
104
120
  trng: false, // Force /dev/urandom usage (w/ fallback)
105
121
  buffer: true, // Return Node.js Buffer (false for Uint8Array)
106
-
122
+
107
123
  // Argon2id Specifics (Requires harden: 3)
108
124
  memoryH: 1, // Number of Argon2id cycles per layer
109
125
  mem: 16384, // Memory cost in KB (default 16MB)
110
126
  passes: 3, // (t) iterations
111
127
  parallelism: 4, // (p) parallel threads
112
-
128
+
113
129
  label: 'THS-Default-v1' // KMAC personalization string
114
130
  };
115
131
 
@@ -122,13 +138,15 @@ const bytes = await THS.random(32, options);
122
138
 
123
139
  ### 🧬 Advanced API
124
140
 
141
+ <br />
142
+
125
143
  #### Direct Utils
126
144
 
127
145
  ```javascript
128
146
  // ESM
129
147
  import Utils from 'ths-csprng/utils';
130
- // CJS
131
- const Utils = require('ths-csprng/utils');
148
+ // or CJS
149
+ const { Utils } = require('ths-csprng/utils');
132
150
 
133
151
  // Get a raw metabolic snapshot (*T=n*)
134
152
  const snap = await Utils.snapshot(2);
@@ -141,17 +159,34 @@ const raw = await THS.raw(32);
141
159
  #### Browser Support
142
160
 
143
161
  ```javascript
144
- const buf = new Uint8Array(32);
145
- 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);
146
170
  ```
147
171
 
148
172
 
149
173
  #### Convenient Aliases
150
174
 
151
175
  ```javascript
152
- 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
181
+
182
+ // Async: Uses THS.random() temporal hardening
183
+ const secureBytes = await randomBytes(
184
+ 32,
185
+ true, // isHardenRNG flag
186
+ { layers: 64, harden: 2 }
187
+ );
153
188
 
154
- await randomBytes(32);
189
+ // Method Aliases
155
190
  await THS.rand(32);
156
191
  await THS.rnd(32);
157
192
  ```
@@ -167,42 +202,110 @@ THS tries to bridge raw hardware chaos with solid cryptographic primitives, forc
167
202
  <br />
168
203
 
169
204
  <div align="center">
170
- <img src="./media/illustration_2.png" alt="Illustration 2" width="1250">
205
+ <img src="https://raw.githubusercontent.com/harnuma9/temporal-hardening-solution-csprng/refs/heads/main/media/illustration_2.png" alt="Illustration 2" width="1250">
171
206
  </div>
172
207
 
173
208
  <br />
174
209
 
175
210
  ## Use Cases
176
211
 
177
- * **Everyday apps:** Use `THS.fillRandom()` or `THS.raw()` for fast, reliable randomness.
212
+ * **Everyday apps:** Use `THS.fillRandom(arr)` or `randomBytes(len)` for **fast, reliable, standard randomness**.
178
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.
179
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.
180
215
 
181
-
182
216
  <br />
183
217
 
184
218
  ## Threat Model
185
219
 
186
- THS is aimed at situations where the attacker might try to:
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:
187
232
 
188
- 1. Reconstruct the historical state of your machine when the key was generated.
189
- 2. Exploit VM snapshots that cause cloned instances to produce identical “random” output.
190
- 3. Bypass or predict hardware entropy sources through backdoors or manufacturer influence.
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.
191
236
 
192
237
  <br />
193
238
 
194
- ## ⚠️ Performance Note
239
+ ### Limitations (What it can’t protect)
195
240
 
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**.
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**.
197
254
 
198
255
  <br />
199
256
 
257
+ ---
258
+
259
+ <br />
260
+
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>
288
+
289
+ <br />
290
+ <br />
291
+ <br />
292
+
200
293
  ## License
201
294
 
202
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.
203
296
 
204
- 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.
205
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 />
206
309
  <br />
207
310
  <br />
208
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":"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","ignoreList":[]}
package/package.json CHANGED
@@ -1,36 +1,46 @@
1
1
  {
2
2
  "name": "ths-csprng",
3
- "version": "1.1.0",
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, createHash, 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
@@ -103,15 +118,22 @@ const Utils = Object.freeze({
103
118
 
104
119
  finally {
105
120
  // wipe result
106
- if (memHard) memHard.fill(0);
107
- if (nonce) nonce.fill(0);
108
- b.fill(0);
121
+ memHard?.fill?.(0);
122
+ nonce?.fill?.(0);
123
+ b?.fill?.(0);
109
124
  }
110
125
  },
111
126
 
127
+
112
128
  /**
113
- * Attempts to harvest raw entropy from the OS device.
114
- * 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.
115
137
  */
116
138
  async getRandom(len, useUrandom) {
117
139
  if (!useUrandom) return randomBytes(len);
@@ -132,8 +154,14 @@ const Utils = Object.freeze({
132
154
  finally { if (fd) await fd.close(); }
133
155
  },
134
156
 
157
+
135
158
  /**
136
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>}
137
165
  */
138
166
  async jitter(useUrandom) {
139
167
  const b = await this.getRandom(64, useUrandom);
@@ -141,9 +169,14 @@ const Utils = Object.freeze({
141
169
  b.fill(0);
142
170
  },
143
171
 
172
+
144
173
  /**
145
- * Optimized BigInt to Buffer conversion with 16-bit Length Header.
146
- * 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.
147
180
  */
148
181
  bigIntToBuffer(n) {
149
182
  if (typeof n !== 'bigint') n = BigInt(n);
@@ -173,8 +206,13 @@ const Utils = Object.freeze({
173
206
  return Buffer.concat([header, data]);
174
207
  },
175
208
 
209
+
176
210
  /**
177
- * 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.
178
216
  */
179
217
  toBytes(input) {
180
218
  if (input instanceof Uint8Array) return input;
@@ -183,5 +221,4 @@ const Utils = Object.freeze({
183
221
  }
184
222
  });
185
223
 
186
- module.exports = Utils;
187
- module.exports.Utils = Utils;
224
+ export default Utils;
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,186 +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, 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;