ths-csprng 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -24
- package/package.json +1 -1
- package/src/util.js +16 -6
- package/src/util.mjs +16 -6
- package/npm-shrinkwrap.json +0 -31
package/README.md
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="./media/banner.png" alt="Banner" width="1250">
|
|
3
|
+
</div>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<br />
|
|
6
|
+
|
|
7
|
+
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
|
+
|
|
9
|
+
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.
|
|
10
|
+
|
|
11
|
+
**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.
|
|
12
|
+
|
|
13
|
+
In short: it uses macroscopic messiness as a defense against microscopic observation.
|
|
4
14
|
|
|
5
15
|
<br />
|
|
6
16
|
|
|
@@ -8,7 +18,27 @@ THS is a **security-hardened CSPRNG wrapper** designed to protect cryptographic
|
|
|
8
18
|
|
|
9
19
|
<br />
|
|
10
20
|
|
|
11
|
-
|
|
21
|
+
<div align="center">
|
|
22
|
+
<img src="./media/illustration_1.png" alt="Illustration 1" width="1250">
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<br />
|
|
26
|
+
|
|
27
|
+
## The “Observer from the Future” Problem
|
|
28
|
+
|
|
29
|
+
Information doesn’t just vanish (see the **[Quantum No-Hiding Theorem](https://en.wikipedia.org/wiki/No-hiding_theorem)**). It spreads into the environment. If an adversary can reconstruct past states—whether through retrocausality, solving hidden variables, or digging through micro-architectural leaks—traditional seeds become vulnerable.
|
|
30
|
+
|
|
31
|
+
Normally, a seed is generated at a single point in time (*T*<sub>0</sub>). If that moment is reconstructed, your entropy is gone.
|
|
32
|
+
|
|
33
|
+
THS fixes this by replacing a single seed with a temporal sequence of many moments (*T*<sub>1</sub>, *T*<sub>2</sub>, *T*<sub>3</sub>, …, *T*<sub>n</sub>). To recover your secret, an attacker doesn’t just need to glance at the past—they have to accurately simulate the entire noisy evolution of the machine across hundreds of layers of timing jitter, memory state, and CPU behavior.
|
|
34
|
+
|
|
35
|
+
<br />
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
<br />
|
|
40
|
+
|
|
41
|
+
### 🚀 Getting Started
|
|
12
42
|
|
|
13
43
|
```bash
|
|
14
44
|
npm install ths-csprng
|
|
@@ -16,7 +46,9 @@ npm install ths-csprng
|
|
|
16
46
|
|
|
17
47
|
<br />
|
|
18
48
|
|
|
19
|
-
|
|
49
|
+
### 🛠️ Quick Start
|
|
50
|
+
|
|
51
|
+
#### Modern ESM (Node.js 18+, Vite, Bun)
|
|
20
52
|
|
|
21
53
|
```javascript
|
|
22
54
|
import THS from 'ths-csprng';
|
|
@@ -24,15 +56,16 @@ import THS from 'ths-csprng';
|
|
|
24
56
|
// Standard 256-bit secure random buffer
|
|
25
57
|
const entropy = await THS.random(32);
|
|
26
58
|
|
|
27
|
-
// High-security hardened key
|
|
59
|
+
// High-security hardened key
|
|
28
60
|
const masterKey = await THS.random(64, {
|
|
29
61
|
layers: 512, // 512 temporal slices
|
|
30
62
|
harden: 3, // Heavy Argon2id hardening
|
|
31
|
-
mem: 65536
|
|
63
|
+
mem: 65536, // 64MB memory cost
|
|
64
|
+
trng: true // Try reading directly from /dev/urandom
|
|
32
65
|
});
|
|
33
66
|
```
|
|
34
67
|
|
|
35
|
-
#### CommonJS
|
|
68
|
+
#### Legacy / Standard CommonJS
|
|
36
69
|
|
|
37
70
|
```javascript
|
|
38
71
|
const THS = require('ths-csprng');
|
|
@@ -47,7 +80,9 @@ const THS = require('ths-csprng');
|
|
|
47
80
|
|
|
48
81
|
---
|
|
49
82
|
|
|
50
|
-
### 💎 Hardening Levels
|
|
83
|
+
### 💎 The Hardening Levels
|
|
84
|
+
|
|
85
|
+
You can dial the security up or down depending on how much “weight” you want the key to have.
|
|
51
86
|
|
|
52
87
|
| Level | Mode | Description |
|
|
53
88
|
| :--- | :--- | :--- |
|
|
@@ -60,20 +95,25 @@ const THS = require('ths-csprng');
|
|
|
60
95
|
|
|
61
96
|
---
|
|
62
97
|
|
|
63
|
-
### ⚙️ Full Configuration
|
|
98
|
+
### ⚙️ Full Configuration Options
|
|
64
99
|
|
|
65
100
|
```javascript
|
|
66
|
-
const
|
|
101
|
+
const options = {
|
|
67
102
|
layers: 128, // Temporal slices (2 to 65536)
|
|
68
103
|
harden: 2, // 0–3 (hardening depth)
|
|
69
|
-
trng: false, // Force /dev/urandom usage
|
|
70
|
-
buffer: true, // Return Node.js Buffer
|
|
104
|
+
trng: false, // Force /dev/urandom usage (w/ fallback)
|
|
105
|
+
buffer: true, // Return Node.js Buffer (false for Uint8Array)
|
|
71
106
|
|
|
72
|
-
// Argon2id Specifics (
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
107
|
+
// Argon2id Specifics (Requires harden: 3)
|
|
108
|
+
memoryH: 1, // Number of Argon2id cycles per layer
|
|
109
|
+
mem: 16384, // Memory cost in KB (default 16MB)
|
|
110
|
+
passes: 3, // (t) iterations
|
|
111
|
+
parallelism: 4, // (p) parallel threads
|
|
112
|
+
|
|
113
|
+
label: 'THS-Default-v1' // KMAC personalization string
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const bytes = await THS.random(32, options);
|
|
77
117
|
```
|
|
78
118
|
|
|
79
119
|
<br />
|
|
@@ -118,15 +158,42 @@ await THS.rnd(32);
|
|
|
118
158
|
|
|
119
159
|
<br />
|
|
120
160
|
|
|
121
|
-
|
|
161
|
+
## Why should you care?
|
|
162
|
+
|
|
163
|
+
Most of the crypto world acts like randomness is a solved problem. But with hardware backdoors, VM cloning, and advanced side-channel attacks, standard entropy sources might be more fragile than they look.
|
|
164
|
+
|
|
165
|
+
THS tries to bridge raw hardware chaos with solid cryptographic primitives, forcing 2<sup>n</sup> security to be exactly as 2<sup>n</sup>, not less.
|
|
166
|
+
|
|
167
|
+
<br />
|
|
168
|
+
|
|
169
|
+
<div align="center">
|
|
170
|
+
<img src="./media/illustration_2.png" alt="Illustration 2" width="1250">
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<br />
|
|
174
|
+
|
|
175
|
+
## Use Cases
|
|
176
|
+
|
|
177
|
+
* **Everyday apps:** Use `THS.fillRandom()` or `THS.raw()` for fast, reliable randomness.
|
|
178
|
+
* **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
|
+
* **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
|
+
|
|
181
|
+
|
|
182
|
+
<br />
|
|
183
|
+
|
|
184
|
+
## Threat Model
|
|
185
|
+
|
|
186
|
+
THS is aimed at situations where the attacker might try to:
|
|
187
|
+
|
|
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.
|
|
191
|
+
|
|
192
|
+
<br />
|
|
122
193
|
|
|
123
|
-
|
|
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.
|
|
194
|
+
## ⚠️ Performance Note
|
|
128
195
|
|
|
129
|
-
**
|
|
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**.
|
|
130
197
|
|
|
131
198
|
<br />
|
|
132
199
|
|
package/package.json
CHANGED
package/src/util.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
const fs = require('node:fs/promises');
|
|
20
20
|
const { promisify } = require('node:util');
|
|
21
|
-
const { randomBytes, timingSafeEqual, argon2: _argon2 } = require('node:crypto');
|
|
21
|
+
const { randomBytes, timingSafeEqual, createHash, argon2: _argon2 } = require('node:crypto');
|
|
22
22
|
|
|
23
23
|
const argon2 = promisify(_argon2);
|
|
24
24
|
const defined_past = process.hrtime.bigint();
|
|
@@ -57,15 +57,25 @@ const Utils = Object.freeze({
|
|
|
57
57
|
BigInt(s || 0) // Sequential salt
|
|
58
58
|
].map(n => this.bigIntToBuffer(n));
|
|
59
59
|
|
|
60
|
-
//
|
|
61
|
-
|
|
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();
|
|
62
69
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
Buffer.allocUnsafe(128),
|
|
70
|
+
// Wipe the noise immediately
|
|
71
|
+
noise.fill(0);
|
|
66
72
|
|
|
73
|
+
// Map metrics to uniquely structured buffers
|
|
74
|
+
const b = Buffer.concat([
|
|
75
|
+
hashedNoise, // This is a COPY of the digest
|
|
67
76
|
...metrics
|
|
68
77
|
]);
|
|
78
|
+
hashedNoise.fill(0); // Wipe the hashed noise
|
|
69
79
|
|
|
70
80
|
if (h <= 2 || s >= mh) return b;
|
|
71
81
|
|
package/src/util.mjs
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import fs from 'node:fs/promises';
|
|
20
20
|
import { promisify } from 'node:util';
|
|
21
|
-
import { randomBytes, timingSafeEqual, argon2 as _argon2 } from 'node:crypto';
|
|
21
|
+
import { randomBytes, timingSafeEqual, createHash, argon2 as _argon2 } from 'node:crypto';
|
|
22
22
|
|
|
23
23
|
const argon2 = promisify(_argon2);
|
|
24
24
|
const defined_past = process.hrtime.bigint();
|
|
@@ -57,15 +57,25 @@ export const Utils = Object.freeze({
|
|
|
57
57
|
BigInt(s || 0) // Sequential salt
|
|
58
58
|
].map(n => this.bigIntToBuffer(n));
|
|
59
59
|
|
|
60
|
-
//
|
|
61
|
-
|
|
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();
|
|
62
69
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
Buffer.allocUnsafe(128),
|
|
70
|
+
// Wipe the noise immediately
|
|
71
|
+
noise.fill(0);
|
|
66
72
|
|
|
73
|
+
// Map metrics to uniquely structured buffers
|
|
74
|
+
const b = Buffer.concat([
|
|
75
|
+
hashedNoise, // This is a COPY of the digest
|
|
67
76
|
...metrics
|
|
68
77
|
]);
|
|
78
|
+
hashedNoise.fill(0); // Wipe the hashed noise
|
|
69
79
|
|
|
70
80
|
if (h <= 2 || s >= mh) return b;
|
|
71
81
|
|
package/npm-shrinkwrap.json
DELETED
|
@@ -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
|
-
}
|