@mindpeeker/entropy 0.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/LICENSE +21 -0
- package/README.md +496 -0
- package/dist/errors.d.ts +14 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +13 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/bits.d.ts +9 -0
- package/dist/internal/bits.d.ts.map +1 -0
- package/dist/internal/bits.js +29 -0
- package/dist/internal/bits.js.map +1 -0
- package/dist/internal/byte-source.d.ts +16 -0
- package/dist/internal/byte-source.d.ts.map +1 -0
- package/dist/internal/byte-source.js +96 -0
- package/dist/internal/byte-source.js.map +1 -0
- package/dist/internal/bytes.d.ts +5 -0
- package/dist/internal/bytes.d.ts.map +1 -0
- package/dist/internal/bytes.js +56 -0
- package/dist/internal/bytes.js.map +1 -0
- package/dist/internal/composite.d.ts +15 -0
- package/dist/internal/composite.d.ts.map +1 -0
- package/dist/internal/composite.js +36 -0
- package/dist/internal/composite.js.map +1 -0
- package/dist/internal/condition.d.ts +38 -0
- package/dist/internal/condition.d.ts.map +1 -0
- package/dist/internal/condition.js +49 -0
- package/dist/internal/condition.js.map +1 -0
- package/dist/internal/encoding.d.ts +14 -0
- package/dist/internal/encoding.d.ts.map +1 -0
- package/dist/internal/encoding.js +91 -0
- package/dist/internal/encoding.js.map +1 -0
- package/dist/internal/health.d.ts +25 -0
- package/dist/internal/health.d.ts.map +1 -0
- package/dist/internal/health.js +84 -0
- package/dist/internal/health.js.map +1 -0
- package/dist/internal/http.d.ts +25 -0
- package/dist/internal/http.d.ts.map +1 -0
- package/dist/internal/http.js +76 -0
- package/dist/internal/http.js.map +1 -0
- package/dist/internal/nist-pulse.d.ts +31 -0
- package/dist/internal/nist-pulse.d.ts.map +1 -0
- package/dist/internal/nist-pulse.js +62 -0
- package/dist/internal/nist-pulse.js.map +1 -0
- package/dist/internal/provider.d.ts +19 -0
- package/dist/internal/provider.d.ts.map +1 -0
- package/dist/internal/provider.js +62 -0
- package/dist/internal/provider.js.map +1 -0
- package/dist/internal/rate-limit.d.ts +15 -0
- package/dist/internal/rate-limit.d.ts.map +1 -0
- package/dist/internal/rate-limit.js +47 -0
- package/dist/internal/rate-limit.js.map +1 -0
- package/dist/internal/sampled.d.ts +25 -0
- package/dist/internal/sampled.d.ts.map +1 -0
- package/dist/internal/sampled.js +49 -0
- package/dist/internal/sampled.js.map +1 -0
- package/dist/internal/stream.d.ts +29 -0
- package/dist/internal/stream.d.ts.map +1 -0
- package/dist/internal/stream.js +78 -0
- package/dist/internal/stream.js.map +1 -0
- package/dist/internal/validate.d.ts +5 -0
- package/dist/internal/validate.d.ts.map +1 -0
- package/dist/internal/validate.js +39 -0
- package/dist/internal/validate.js.map +1 -0
- package/dist/node/ffmpeg-frames.d.ts +27 -0
- package/dist/node/ffmpeg-frames.d.ts.map +1 -0
- package/dist/node/ffmpeg-frames.js +102 -0
- package/dist/node/ffmpeg-frames.js.map +1 -0
- package/dist/node/ffmpeg-samples.d.ts +21 -0
- package/dist/node/ffmpeg-samples.d.ts.map +1 -0
- package/dist/node/ffmpeg-samples.js +81 -0
- package/dist/node/ffmpeg-samples.js.map +1 -0
- package/dist/node/hwrng.d.ts +12 -0
- package/dist/node/hwrng.d.ts.map +1 -0
- package/dist/node/hwrng.js +43 -0
- package/dist/node/hwrng.js.map +1 -0
- package/dist/node/index.d.ts +9 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +7 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/rtl-sdr.d.ts +31 -0
- package/dist/node/rtl-sdr.d.ts.map +1 -0
- package/dist/node/rtl-sdr.js +63 -0
- package/dist/node/rtl-sdr.js.map +1 -0
- package/dist/node/serial-source.d.ts +22 -0
- package/dist/node/serial-source.d.ts.map +1 -0
- package/dist/node/serial-source.js +63 -0
- package/dist/node/serial-source.js.map +1 -0
- package/dist/providers/anu-legacy.d.ts +17 -0
- package/dist/providers/anu-legacy.d.ts.map +1 -0
- package/dist/providers/anu-legacy.js +56 -0
- package/dist/providers/anu-legacy.js.map +1 -0
- package/dist/providers/anu.d.ts +14 -0
- package/dist/providers/anu.d.ts.map +1 -0
- package/dist/providers/anu.js +42 -0
- package/dist/providers/anu.js.map +1 -0
- package/dist/providers/bitcoin.d.ts +14 -0
- package/dist/providers/bitcoin.d.ts.map +1 -0
- package/dist/providers/bitcoin.js +95 -0
- package/dist/providers/bitcoin.js.map +1 -0
- package/dist/providers/camera.d.ts +49 -0
- package/dist/providers/camera.d.ts.map +1 -0
- package/dist/providers/camera.js +123 -0
- package/dist/providers/camera.js.map +1 -0
- package/dist/providers/crypto.d.ts +8 -0
- package/dist/providers/crypto.d.ts.map +1 -0
- package/dist/providers/crypto.js +26 -0
- package/dist/providers/crypto.js.map +1 -0
- package/dist/providers/curby.d.ts +17 -0
- package/dist/providers/curby.d.ts.map +1 -0
- package/dist/providers/curby.js +113 -0
- package/dist/providers/curby.js.map +1 -0
- package/dist/providers/drand.d.ts +19 -0
- package/dist/providers/drand.d.ts.map +1 -0
- package/dist/providers/drand.js +87 -0
- package/dist/providers/drand.js.map +1 -0
- package/dist/providers/flow.d.ts +15 -0
- package/dist/providers/flow.d.ts.map +1 -0
- package/dist/providers/flow.js +82 -0
- package/dist/providers/flow.js.map +1 -0
- package/dist/providers/index.d.ts +56 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +32 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/inmetro.d.ts +17 -0
- package/dist/providers/inmetro.d.ts.map +1 -0
- package/dist/providers/inmetro.js +25 -0
- package/dist/providers/inmetro.js.map +1 -0
- package/dist/providers/jitter.d.ts +33 -0
- package/dist/providers/jitter.d.ts.map +1 -0
- package/dist/providers/jitter.js +81 -0
- package/dist/providers/jitter.js.map +1 -0
- package/dist/providers/lfdr.d.ts +11 -0
- package/dist/providers/lfdr.d.ts.map +1 -0
- package/dist/providers/lfdr.js +34 -0
- package/dist/providers/lfdr.js.map +1 -0
- package/dist/providers/microphone.d.ts +26 -0
- package/dist/providers/microphone.d.ts.map +1 -0
- package/dist/providers/microphone.js +120 -0
- package/dist/providers/microphone.js.map +1 -0
- package/dist/providers/nist-beacon.d.ts +11 -0
- package/dist/providers/nist-beacon.d.ts.map +1 -0
- package/dist/providers/nist-beacon.js +20 -0
- package/dist/providers/nist-beacon.js.map +1 -0
- package/dist/providers/nqsn.d.ts +11 -0
- package/dist/providers/nqsn.d.ts.map +1 -0
- package/dist/providers/nqsn.js +16 -0
- package/dist/providers/nqsn.js.map +1 -0
- package/dist/providers/outshift.d.ts +14 -0
- package/dist/providers/outshift.d.ts.map +1 -0
- package/dist/providers/outshift.js +52 -0
- package/dist/providers/outshift.js.map +1 -0
- package/dist/providers/padova.d.ts +12 -0
- package/dist/providers/padova.d.ts.map +1 -0
- package/dist/providers/padova.js +56 -0
- package/dist/providers/padova.js.map +1 -0
- package/dist/providers/qbck.d.ts +13 -0
- package/dist/providers/qbck.d.ts.map +1 -0
- package/dist/providers/qbck.js +62 -0
- package/dist/providers/qbck.js.map +1 -0
- package/dist/providers/qci.d.ts +14 -0
- package/dist/providers/qci.d.ts.map +1 -0
- package/dist/providers/qci.js +91 -0
- package/dist/providers/qci.js.map +1 -0
- package/dist/providers/qrandom.d.ts +12 -0
- package/dist/providers/qrandom.d.ts.map +1 -0
- package/dist/providers/qrandom.js +39 -0
- package/dist/providers/qrandom.js.map +1 -0
- package/dist/providers/randao.d.ts +14 -0
- package/dist/providers/randao.d.ts.map +1 -0
- package/dist/providers/randao.js +81 -0
- package/dist/providers/randao.js.map +1 -0
- package/dist/providers/random-org.d.ts +13 -0
- package/dist/providers/random-org.d.ts.map +1 -0
- package/dist/providers/random-org.js +89 -0
- package/dist/providers/random-org.js.map +1 -0
- package/dist/providers/sdr.d.ts +21 -0
- package/dist/providers/sdr.d.ts.map +1 -0
- package/dist/providers/sdr.js +63 -0
- package/dist/providers/sdr.js.map +1 -0
- package/dist/providers/sensor.d.ts +24 -0
- package/dist/providers/sensor.d.ts.map +1 -0
- package/dist/providers/sensor.js +119 -0
- package/dist/providers/sensor.js.map +1 -0
- package/dist/providers/serial.d.ts +35 -0
- package/dist/providers/serial.d.ts.map +1 -0
- package/dist/providers/serial.js +78 -0
- package/dist/providers/serial.js.map +1 -0
- package/dist/providers/solana.d.ts +13 -0
- package/dist/providers/solana.d.ts.map +1 -0
- package/dist/providers/solana.js +83 -0
- package/dist/providers/solana.js.map +1 -0
- package/dist/providers/superrand.d.ts +35 -0
- package/dist/providers/superrand.d.ts.map +1 -0
- package/dist/providers/superrand.js +215 -0
- package/dist/providers/superrand.js.map +1 -0
- package/dist/providers/tezos.d.ts +13 -0
- package/dist/providers/tezos.d.ts.map +1 -0
- package/dist/providers/tezos.js +92 -0
- package/dist/providers/tezos.js.map +1 -0
- package/dist/providers/uchile.d.ts +11 -0
- package/dist/providers/uchile.d.ts.map +1 -0
- package/dist/providers/uchile.js +22 -0
- package/dist/providers/uchile.js.map +1 -0
- package/dist/strategies/fallback.d.ts +12 -0
- package/dist/strategies/fallback.d.ts.map +1 -0
- package/dist/strategies/fallback.js +45 -0
- package/dist/strategies/fallback.js.map +1 -0
- package/dist/strategies/race.d.ts +7 -0
- package/dist/strategies/race.d.ts.map +1 -0
- package/dist/strategies/race.js +41 -0
- package/dist/strategies/race.js.map +1 -0
- package/dist/strategies/xor.d.ts +9 -0
- package/dist/strategies/xor.d.ts.map +1 -0
- package/dist/strategies/xor.js +56 -0
- package/dist/strategies/xor.js.map +1 -0
- package/dist/types.d.ts +36 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { EntropyProvider } from '../types.js';
|
|
2
|
+
export interface RandaoOptions {
|
|
3
|
+
fetch?: typeof fetch;
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
pollIntervalMs?: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Ethereum beacon-chain RANDAO mix via a keyless public node. PUBLIC
|
|
9
|
+
* crypto-beacon randomness — the block proposer can bias roughly one bit per
|
|
10
|
+
* slot by withholding, so treat it as auditable public randomness for
|
|
11
|
+
* commitments and mixing, never as a private entropy source.
|
|
12
|
+
*/
|
|
13
|
+
export declare function randao(opts?: RandaoOptions): EntropyProvider;
|
|
14
|
+
//# sourceMappingURL=randao.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"randao.d.ts","sourceRoot":"","sources":["../../src/providers/randao.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAqB,MAAM,aAAa,CAAA;AAarE,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAUD;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,IAAI,GAAE,aAAkB,GAAG,eAAe,CA8EhE"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { EntropyError } from '../errors.js';
|
|
2
|
+
import { concatBytes } from '../internal/bytes.js';
|
|
3
|
+
import { fetchJson } from '../internal/http.js';
|
|
4
|
+
import { defineProvider } from '../internal/provider.js';
|
|
5
|
+
import { beaconStream } from '../internal/stream.js';
|
|
6
|
+
import { bytesFromHexField } from '../internal/validate.js';
|
|
7
|
+
const INFO = Object.freeze({
|
|
8
|
+
name: 'randao',
|
|
9
|
+
kind: 'beacon',
|
|
10
|
+
privacy: 'public',
|
|
11
|
+
});
|
|
12
|
+
const MIX_BYTES = 32;
|
|
13
|
+
const DEFAULT_BASE_URL = 'https://ethereum-beacon-api.publicnode.com';
|
|
14
|
+
const SLOTS_PER_EPOCH = 32;
|
|
15
|
+
// One slot every 12 seconds.
|
|
16
|
+
const DEFAULT_POLL_INTERVAL_MS = 12_000;
|
|
17
|
+
/**
|
|
18
|
+
* Ethereum beacon-chain RANDAO mix via a keyless public node. PUBLIC
|
|
19
|
+
* crypto-beacon randomness — the block proposer can bias roughly one bit per
|
|
20
|
+
* slot by withholding, so treat it as auditable public randomness for
|
|
21
|
+
* commitments and mixing, never as a private entropy source.
|
|
22
|
+
*/
|
|
23
|
+
export function randao(opts = {}) {
|
|
24
|
+
const { baseUrl = DEFAULT_BASE_URL, pollIntervalMs = DEFAULT_POLL_INTERVAL_MS, fetch: fetchImpl, } = opts;
|
|
25
|
+
async function fetchMix(query, signal) {
|
|
26
|
+
const res = await fetchJson(`${baseUrl}/eth/v1/beacon/states/head/randao${query}`, { provider: INFO.name, signal, fetchImpl });
|
|
27
|
+
const randaoHex = res?.data?.randao;
|
|
28
|
+
if (typeof randaoHex !== 'string' || !randaoHex.startsWith('0x')) {
|
|
29
|
+
throw new EntropyError('bad_response', 'missing randao mix', { provider: INFO.name });
|
|
30
|
+
}
|
|
31
|
+
return { hex: randaoHex, bytes: bytesFromHexField(randaoHex.slice(2), MIX_BYTES, INFO.name) };
|
|
32
|
+
}
|
|
33
|
+
async function fetchHeadEpoch(signal) {
|
|
34
|
+
const res = await fetchJson(`${baseUrl}/eth/v1/beacon/headers/head`, {
|
|
35
|
+
provider: INFO.name,
|
|
36
|
+
signal,
|
|
37
|
+
fetchImpl,
|
|
38
|
+
});
|
|
39
|
+
const slot = Number(res?.data?.header?.message?.slot);
|
|
40
|
+
if (!Number.isInteger(slot)) {
|
|
41
|
+
throw new EntropyError('bad_response', 'missing head slot', { provider: INFO.name });
|
|
42
|
+
}
|
|
43
|
+
return Math.floor(slot / SLOTS_PER_EPOCH);
|
|
44
|
+
}
|
|
45
|
+
return defineProvider({
|
|
46
|
+
...INFO,
|
|
47
|
+
defaultChunkBytes: MIX_BYTES,
|
|
48
|
+
async getBytes(length, reqOpts) {
|
|
49
|
+
const mixesNeeded = Math.ceil(length / MIX_BYTES);
|
|
50
|
+
const chunks = [(await fetchMix('', reqOpts?.signal)).bytes];
|
|
51
|
+
if (mixesNeeded > 1) {
|
|
52
|
+
// Historical mixes are equally public — walk epochs backwards.
|
|
53
|
+
const headEpoch = await fetchHeadEpoch(reqOpts?.signal);
|
|
54
|
+
for (let i = 1; i < mixesNeeded; i++) {
|
|
55
|
+
const epoch = headEpoch - i;
|
|
56
|
+
if (epoch < 0) {
|
|
57
|
+
throw new EntropyError('insufficient_entropy', 'not enough epochs for request', {
|
|
58
|
+
provider: INFO.name,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
chunks.push((await fetchMix(`?epoch=${epoch}`, reqOpts?.signal)).bytes);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return { bytes: concatBytes(chunks).slice(0, length), sources: [INFO] };
|
|
65
|
+
},
|
|
66
|
+
stream(streamOpts = {}) {
|
|
67
|
+
// no numeric round id on this endpoint — dedupe by value change
|
|
68
|
+
let lastHex = '';
|
|
69
|
+
let seq = 0;
|
|
70
|
+
return beaconStream(async (signal) => {
|
|
71
|
+
const mix = await fetchMix('', signal);
|
|
72
|
+
if (mix.hex !== lastHex) {
|
|
73
|
+
lastHex = mix.hex;
|
|
74
|
+
seq++;
|
|
75
|
+
}
|
|
76
|
+
return { id: seq, bytes: mix.bytes };
|
|
77
|
+
}, pollIntervalMs, MIX_BYTES, INFO.name, streamOpts);
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=randao.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"randao.js","sourceRoot":"","sources":["../../src/providers/randao.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { EntropyProvider } from '../types.js';
|
|
2
|
+
export interface RandomOrgOptions {
|
|
3
|
+
/** RANDOM.ORG API key (free Developer tier: 1000 req/day, 250k bits/day). */
|
|
4
|
+
apiKey: string;
|
|
5
|
+
fetch?: typeof fetch;
|
|
6
|
+
baseUrl?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* RANDOM.ORG (atmospheric radio noise) via the JSON-RPC 4.0 Basic API's
|
|
10
|
+
* `generateBlobs`. Honors the server's `advisoryDelay` between requests.
|
|
11
|
+
*/
|
|
12
|
+
export declare function randomOrg(opts: RandomOrgOptions): EntropyProvider;
|
|
13
|
+
//# sourceMappingURL=random-org.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"random-org.d.ts","sourceRoot":"","sources":["../../src/providers/random-org.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAqB,MAAM,aAAa,CAAA;AAWrE,MAAM,WAAW,gBAAgB;IAC/B,6EAA6E;IAC7E,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAuBD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,gBAAgB,GAAG,eAAe,CAuDjE"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { EntropyError } from '../errors.js';
|
|
2
|
+
import { base64ToBytes, concatBytes } from '../internal/bytes.js';
|
|
3
|
+
import { fetchJson } from '../internal/http.js';
|
|
4
|
+
import { defineProvider } from '../internal/provider.js';
|
|
5
|
+
import { MinIntervalGate } from '../internal/rate-limit.js';
|
|
6
|
+
const INFO = Object.freeze({
|
|
7
|
+
name: 'random.org',
|
|
8
|
+
kind: 'trng',
|
|
9
|
+
privacy: 'private',
|
|
10
|
+
});
|
|
11
|
+
// generateBlobs caps a request at 2^20 bits total.
|
|
12
|
+
const MAX_BYTES_PER_REQUEST = 131_072;
|
|
13
|
+
const DEFAULT_BASE_URL = 'https://api.random.org/json-rpc/4/invoke';
|
|
14
|
+
function rpcError(error) {
|
|
15
|
+
const code = error.code ?? 0;
|
|
16
|
+
const message = `RANDOM.ORG error ${code}: ${error.message ?? 'unknown'}`;
|
|
17
|
+
// Best-effort mapping of RANDOM.ORG's app-level codes: 400/401 are key
|
|
18
|
+
// problems, 402/403 are exhausted daily allowances.
|
|
19
|
+
if (code === 400 || code === 401)
|
|
20
|
+
return new EntropyError('auth', message, { provider: INFO.name });
|
|
21
|
+
if (code === 402 || code === 403) {
|
|
22
|
+
return new EntropyError('rate_limited', message, { provider: INFO.name });
|
|
23
|
+
}
|
|
24
|
+
return new EntropyError('bad_response', message, { provider: INFO.name });
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* RANDOM.ORG (atmospheric radio noise) via the JSON-RPC 4.0 Basic API's
|
|
28
|
+
* `generateBlobs`. Honors the server's `advisoryDelay` between requests.
|
|
29
|
+
*/
|
|
30
|
+
export function randomOrg(opts) {
|
|
31
|
+
if (!opts?.apiKey)
|
|
32
|
+
throw new TypeError('randomOrg({ apiKey }) requires a non-empty apiKey');
|
|
33
|
+
const { apiKey, baseUrl = DEFAULT_BASE_URL, fetch: fetchImpl } = opts;
|
|
34
|
+
const gate = new MinIntervalGate(0);
|
|
35
|
+
let nextId = 1;
|
|
36
|
+
return defineProvider({
|
|
37
|
+
...INFO,
|
|
38
|
+
async getBytes(length, reqOpts) {
|
|
39
|
+
const chunks = [];
|
|
40
|
+
for (let remaining = length; remaining > 0;) {
|
|
41
|
+
const n = Math.min(MAX_BYTES_PER_REQUEST, remaining);
|
|
42
|
+
await gate.wait(reqOpts?.signal);
|
|
43
|
+
const res = await fetchJson(baseUrl, {
|
|
44
|
+
provider: INFO.name,
|
|
45
|
+
method: 'POST',
|
|
46
|
+
body: {
|
|
47
|
+
jsonrpc: '2.0',
|
|
48
|
+
method: 'generateBlobs',
|
|
49
|
+
params: { apiKey, n: 1, size: n * 8, format: 'base64' },
|
|
50
|
+
id: nextId++,
|
|
51
|
+
},
|
|
52
|
+
signal: reqOpts?.signal,
|
|
53
|
+
fetchImpl,
|
|
54
|
+
});
|
|
55
|
+
if (res?.error)
|
|
56
|
+
throw rpcError(res.error);
|
|
57
|
+
const advisoryDelay = res?.result?.advisoryDelay;
|
|
58
|
+
if (typeof advisoryDelay === 'number' && advisoryDelay > 0)
|
|
59
|
+
gate.defer(advisoryDelay);
|
|
60
|
+
const data = res?.result?.random?.data;
|
|
61
|
+
const blob = Array.isArray(data) ? data[0] : undefined;
|
|
62
|
+
if (typeof blob !== 'string') {
|
|
63
|
+
throw new EntropyError('bad_response', 'missing blob in generateBlobs result', {
|
|
64
|
+
provider: INFO.name,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
let bytes;
|
|
68
|
+
try {
|
|
69
|
+
bytes = base64ToBytes(blob);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
throw new EntropyError('bad_response', 'invalid base64 blob', {
|
|
73
|
+
provider: INFO.name,
|
|
74
|
+
cause: error,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (bytes.length !== n) {
|
|
78
|
+
throw new EntropyError('bad_response', `expected ${n} bytes, got ${bytes.length}`, {
|
|
79
|
+
provider: INFO.name,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
chunks.push(bytes);
|
|
83
|
+
remaining -= n;
|
|
84
|
+
}
|
|
85
|
+
return { bytes: concatBytes(chunks), sources: [INFO] };
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=random-org.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"random-org.js","sourceRoot":"","sources":["../../src/providers/random-org.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ByteSource } from '../internal/byte-source.js';
|
|
2
|
+
import type { ConditioningOptions } from '../internal/condition.js';
|
|
3
|
+
import type { EntropyProvider } from '../types.js';
|
|
4
|
+
export interface SdrOptions extends ConditioningOptions {
|
|
5
|
+
/** Raw IQ bytes, e.g. from rtlSdrSource (@mindpeeker/entropy/node). REQUIRED. */
|
|
6
|
+
source: ByteSource;
|
|
7
|
+
/** Von Neumann debiasing over the LSB stream (rtl-entropy prior art). Default true. */
|
|
8
|
+
debias?: boolean;
|
|
9
|
+
/** IQ bytes discarded at session start while tuner gain settles. Default 4096. */
|
|
10
|
+
warmupBytes?: number;
|
|
11
|
+
}
|
|
12
|
+
/** The 6 least significant bits of each raw IQ byte (top bits carry signal, not noise). */
|
|
13
|
+
export declare function iqLsbBits(chunk: Uint8Array): number[];
|
|
14
|
+
/**
|
|
15
|
+
* Software-defined-radio noise (RTL-SDR dongles): front-end thermal noise in
|
|
16
|
+
* the ADC's low bits, tuned to quiet spectrum at max manual gain. COMMUNITY-
|
|
17
|
+
* VERIFIED tier — RF injection attacks are demonstrated in the literature,
|
|
18
|
+
* so treat this as a mixing source, never a sole root of trust.
|
|
19
|
+
*/
|
|
20
|
+
export declare function sdrEntropy(opts: SdrOptions): EntropyProvider;
|
|
21
|
+
//# sourceMappingURL=sdr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdr.d.ts","sourceRoot":"","sources":["../../src/providers/sdr.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,UAAU,EAAiC,MAAM,4BAA4B,CAAA;AAC3F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAEnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAElD,MAAM,WAAW,UAAW,SAAQ,mBAAmB;IACrD,iFAAiF;IACjF,MAAM,EAAE,UAAU,CAAA;IAClB,uFAAuF;IACvF,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,2FAA2F;AAC3F,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,EAAE,CAMrD;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,eAAe,CAmD5D"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { packBits, vonNeumann } from '../internal/bits.js';
|
|
2
|
+
import { iterateBytes, persistentBytes } from '../internal/byte-source.js';
|
|
3
|
+
import { sampledProvider } from '../internal/sampled.js';
|
|
4
|
+
/** The 6 least significant bits of each raw IQ byte (top bits carry signal, not noise). */
|
|
5
|
+
export function iqLsbBits(chunk) {
|
|
6
|
+
const bits = [];
|
|
7
|
+
for (const byte of chunk) {
|
|
8
|
+
for (let b = 5; b >= 0; b--)
|
|
9
|
+
bits.push((byte >> b) & 1);
|
|
10
|
+
}
|
|
11
|
+
return bits;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Software-defined-radio noise (RTL-SDR dongles): front-end thermal noise in
|
|
15
|
+
* the ADC's low bits, tuned to quiet spectrum at max manual gain. COMMUNITY-
|
|
16
|
+
* VERIFIED tier — RF injection attacks are demonstrated in the literature,
|
|
17
|
+
* so treat this as a mixing source, never a sole root of trust.
|
|
18
|
+
*/
|
|
19
|
+
export function sdrEntropy(opts) {
|
|
20
|
+
if (!opts?.source) {
|
|
21
|
+
throw new TypeError('sdrEntropy requires a { source } of raw IQ bytes — in Node use rtlSdrSource from @mindpeeker/entropy/node');
|
|
22
|
+
}
|
|
23
|
+
const { source, debias = true, warmupBytes = 4096 } = opts;
|
|
24
|
+
// The injected source is persistent: sessions read a shared view, warmup
|
|
25
|
+
// and bit extraction are applied once for the source's lifetime, and
|
|
26
|
+
// closing a session never closes the dongle stream (its owner does).
|
|
27
|
+
const shared = persistentBytes((async function* extracted() {
|
|
28
|
+
let skipped = 0;
|
|
29
|
+
let leftover = [];
|
|
30
|
+
for await (const chunk of iterateBytes(source)) {
|
|
31
|
+
let iq = chunk;
|
|
32
|
+
if (skipped < warmupBytes) {
|
|
33
|
+
const need = warmupBytes - skipped;
|
|
34
|
+
if (chunk.length <= need) {
|
|
35
|
+
skipped += chunk.length;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
skipped = warmupBytes;
|
|
39
|
+
iq = chunk.slice(need);
|
|
40
|
+
}
|
|
41
|
+
let bits = iqLsbBits(iq);
|
|
42
|
+
if (debias)
|
|
43
|
+
bits = vonNeumann(bits);
|
|
44
|
+
const [packed, rest] = packBits(leftover.concat(bits));
|
|
45
|
+
leftover = rest;
|
|
46
|
+
if (packed.length > 0)
|
|
47
|
+
yield packed;
|
|
48
|
+
}
|
|
49
|
+
})());
|
|
50
|
+
async function* open(signal) {
|
|
51
|
+
yield* shared(signal);
|
|
52
|
+
}
|
|
53
|
+
return sampledProvider({
|
|
54
|
+
name: 'sdr',
|
|
55
|
+
kind: 'trng',
|
|
56
|
+
privacy: 'private',
|
|
57
|
+
open,
|
|
58
|
+
defaultMinEntropyPerSample: 1,
|
|
59
|
+
defaultSafetyFactor: 4,
|
|
60
|
+
defaultTimeoutMs: 30_000,
|
|
61
|
+
}, opts);
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=sdr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdr.js","sourceRoot":"","sources":["../../src/providers/sdr.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,EAAmB,YAAY,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAE3F,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAYxD,2FAA2F;AAC3F,MAAM,UAAU,SAAS,CAAC,KAAiB;IACzC,MAAM,IAAI,GAAa,EAAE,CAAA;IACzB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE;YAAE,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;IACzD,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,IAAgB;IACzC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;QAClB,MAAM,IAAI,SAAS,CACjB,2GAA2G,CAC5G,CAAA;IACH,CAAC;IACD,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,WAAW,GAAG,IAAI,EAAE,GAAG,IAAI,CAAA;IAE1D,yEAAyE;IACzE,qEAAqE;IACrE,qEAAqE;IACrE,MAAM,MAAM,GAAG,eAAe,CAC5B,CAAC,KAAK,SAAS,CAAC,CAAC,SAAS;QACxB,IAAI,OAAO,GAAG,CAAC,CAAA;QACf,IAAI,QAAQ,GAAa,EAAE,CAAA;QAC3B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;YAC/C,IAAI,EAAE,GAAG,KAAK,CAAA;YACd,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;gBAC1B,MAAM,IAAI,GAAG,WAAW,GAAG,OAAO,CAAA;gBAClC,IAAI,KAAK,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;oBACzB,OAAO,IAAI,KAAK,CAAC,MAAM,CAAA;oBACvB,SAAQ;gBACV,CAAC;gBACD,OAAO,GAAG,WAAW,CAAA;gBACrB,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YACxB,CAAC;YACD,IAAI,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC,CAAA;YACxB,IAAI,MAAM;gBAAE,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;YACnC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;YACtD,QAAQ,GAAG,IAAI,CAAA;YACf,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;gBAAE,MAAM,MAAM,CAAA;QACrC,CAAC;IACH,CAAC,CAAC,EAAE,CACL,CAAA;IAED,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,MAAoB;QACvC,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACvB,CAAC;IAED,OAAO,eAAe,CACpB;QACE,IAAI,EAAE,KAAK;QACX,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,SAAS;QAClB,IAAI;QACJ,0BAA0B,EAAE,CAAC;QAC7B,mBAAmB,EAAE,CAAC;QACtB,gBAAgB,EAAE,MAAM;KACzB,EACD,IAAI,CACL,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ConditioningOptions } from '../internal/condition.js';
|
|
2
|
+
import type { EntropyProvider } from '../types.js';
|
|
3
|
+
export interface SensorSource {
|
|
4
|
+
/** One reading per motion event: available axes, e.g. [ax, ay, az, gx, gy, gz]. */
|
|
5
|
+
samples(signal?: AbortSignal): AsyncIterable<Float64Array | number[]>;
|
|
6
|
+
close?(): void | Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
export interface SensorOptions extends ConditioningOptions {
|
|
9
|
+
/** Injected readings (tests, non-browser runtimes). Default: browser motion sensors. */
|
|
10
|
+
source?: SensorSource;
|
|
11
|
+
/** Generic Sensor API frequency hint. Default 60. */
|
|
12
|
+
frequency?: number;
|
|
13
|
+
/** Readings discarded at session start. Default 30. */
|
|
14
|
+
warmupSamples?: number;
|
|
15
|
+
}
|
|
16
|
+
/** One byte per axis: scale the reading and keep the low byte. */
|
|
17
|
+
export declare function sensorReadingBytes(reading: ArrayLike<number>, scale?: number): Uint8Array;
|
|
18
|
+
/**
|
|
19
|
+
* Motion-sensor noise (accelerometer/gyroscope). Browser readings are
|
|
20
|
+
* privacy-quantized, so the credited entropy is deliberately tiny — this is a
|
|
21
|
+
* breadth source for phones/tablets, best mixed via `xorMix`, not a fast one.
|
|
22
|
+
*/
|
|
23
|
+
export declare function sensorEntropy(opts?: SensorOptions): EntropyProvider;
|
|
24
|
+
//# sourceMappingURL=sensor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sensor.d.ts","sourceRoot":"","sources":["../../src/providers/sensor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAEnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAElD,MAAM,WAAW,YAAY;IAC3B,mFAAmF;IACnF,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,aAAa,CAAC,YAAY,GAAG,MAAM,EAAE,CAAC,CAAA;IACrE,KAAK,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC/B;AAED,MAAM,WAAW,aAAc,SAAQ,mBAAmB;IACxD,wFAAwF;IACxF,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,uDAAuD;IACvD,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,kEAAkE;AAClE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,KAAK,SAAM,GAAG,UAAU,CAMtF;AAgFD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,GAAE,aAAkB,GAAG,eAAe,CAwCvE"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { packBits } from '../internal/bits.js';
|
|
2
|
+
import { sampledProvider } from '../internal/sampled.js';
|
|
3
|
+
/** One byte per axis: scale the reading and keep the low byte. */
|
|
4
|
+
export function sensorReadingBytes(reading, scale = 100) {
|
|
5
|
+
const out = new Uint8Array(reading.length);
|
|
6
|
+
for (let i = 0; i < reading.length; i++) {
|
|
7
|
+
out[i] = Math.round(reading[i] * scale) & 0xff;
|
|
8
|
+
}
|
|
9
|
+
return out;
|
|
10
|
+
}
|
|
11
|
+
function browserSensorSource(frequency) {
|
|
12
|
+
return {
|
|
13
|
+
async *samples(signal) {
|
|
14
|
+
const g = globalThis;
|
|
15
|
+
const queue = [];
|
|
16
|
+
let notify = null;
|
|
17
|
+
const push = (reading) => {
|
|
18
|
+
queue.push(reading);
|
|
19
|
+
notify?.();
|
|
20
|
+
};
|
|
21
|
+
let cleanup;
|
|
22
|
+
if (g.Accelerometer) {
|
|
23
|
+
// Generic Sensor API (Chromium): readings are quantized to 0.1 m/s² /
|
|
24
|
+
// 0.1 °/s — the conservative credit accounts for that.
|
|
25
|
+
const sensors = [new g.Accelerometer({ frequency })];
|
|
26
|
+
if (g.Gyroscope)
|
|
27
|
+
sensors.push(new g.Gyroscope({ frequency }));
|
|
28
|
+
for (const sensor of sensors) {
|
|
29
|
+
sensor.addEventListener('reading', () => {
|
|
30
|
+
push(sensors.flatMap((s) => [s.x ?? 0, s.y ?? 0, s.z ?? 0]));
|
|
31
|
+
});
|
|
32
|
+
sensor.start();
|
|
33
|
+
}
|
|
34
|
+
cleanup = () => {
|
|
35
|
+
for (const sensor of sensors)
|
|
36
|
+
sensor.stop();
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
else if (typeof g.addEventListener === 'function' && 'DeviceMotionEvent' in g) {
|
|
40
|
+
// iOS requires an explicit permission request from a user gesture.
|
|
41
|
+
await g.DeviceMotionEvent?.requestPermission?.();
|
|
42
|
+
const onMotion = (event) => {
|
|
43
|
+
const a = event.accelerationIncludingGravity;
|
|
44
|
+
const r = event.rotationRate;
|
|
45
|
+
push([a?.x ?? 0, a?.y ?? 0, a?.z ?? 0, r?.alpha ?? 0, r?.beta ?? 0, r?.gamma ?? 0]);
|
|
46
|
+
};
|
|
47
|
+
g.addEventListener('devicemotion', onMotion);
|
|
48
|
+
cleanup = () => g.removeEventListener?.('devicemotion', onMotion);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
throw new TypeError('sensorEntropy: no motion sensors in this runtime — pass a { source }');
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
while (true) {
|
|
55
|
+
if (signal?.aborted)
|
|
56
|
+
throw signal.reason;
|
|
57
|
+
const reading = queue.shift();
|
|
58
|
+
if (reading) {
|
|
59
|
+
yield reading;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
await new Promise((resolve) => {
|
|
63
|
+
notify = resolve;
|
|
64
|
+
});
|
|
65
|
+
notify = null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
cleanup();
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Motion-sensor noise (accelerometer/gyroscope). Browser readings are
|
|
77
|
+
* privacy-quantized, so the credited entropy is deliberately tiny — this is a
|
|
78
|
+
* breadth source for phones/tablets, best mixed via `xorMix`, not a fast one.
|
|
79
|
+
*/
|
|
80
|
+
export function sensorEntropy(opts = {}) {
|
|
81
|
+
const { frequency = 60, warmupSamples = 30 } = opts;
|
|
82
|
+
const source = opts.source ?? browserSensorSource(frequency);
|
|
83
|
+
async function* open(signal) {
|
|
84
|
+
let skipped = 0;
|
|
85
|
+
let leftover = [];
|
|
86
|
+
for await (const reading of source.samples(signal)) {
|
|
87
|
+
if (signal?.aborted)
|
|
88
|
+
throw signal.reason ?? new DOMException('aborted', 'AbortError');
|
|
89
|
+
if (skipped < warmupSamples) {
|
|
90
|
+
skipped++;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const bytes = sensorReadingBytes(reading);
|
|
94
|
+
const bits = leftover;
|
|
95
|
+
for (const byte of bytes) {
|
|
96
|
+
for (let b = 7; b >= 0; b--)
|
|
97
|
+
bits.push((byte >> b) & 1);
|
|
98
|
+
}
|
|
99
|
+
const [packed, rest] = packBits(bits);
|
|
100
|
+
leftover = rest;
|
|
101
|
+
if (packed.length > 0)
|
|
102
|
+
yield packed;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return sampledProvider({
|
|
106
|
+
name: 'sensor',
|
|
107
|
+
kind: 'trng',
|
|
108
|
+
privacy: 'private',
|
|
109
|
+
open,
|
|
110
|
+
// ≤1 bit per quantized reading (≈6 raw bytes) → 0.25 b/B is already generous
|
|
111
|
+
defaultMinEntropyPerSample: 0.25,
|
|
112
|
+
// …but health-test at H=1 so a frozen device's repeating pattern trips
|
|
113
|
+
// the APT instead of sliding under a disabled cutoff
|
|
114
|
+
defaultHealthMinEntropyPerSample: 1,
|
|
115
|
+
defaultSafetyFactor: 4,
|
|
116
|
+
defaultTimeoutMs: 60_000, // permission prompt + slow accumulation
|
|
117
|
+
}, opts);
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=sensor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sensor.js","sourceRoot":"","sources":["../../src/providers/sensor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type ByteSource } from '../internal/byte-source.js';
|
|
2
|
+
import type { ConditioningOptions } from '../internal/condition.js';
|
|
3
|
+
import type { EntropyProvider } from '../types.js';
|
|
4
|
+
/** The Web Serial SerialPort surface this provider needs (injectable). */
|
|
5
|
+
export interface SerialPortLike {
|
|
6
|
+
open(options: {
|
|
7
|
+
baudRate: number;
|
|
8
|
+
}): Promise<void>;
|
|
9
|
+
close(): Promise<void>;
|
|
10
|
+
readable: ReadableStream<Uint8Array> | null;
|
|
11
|
+
writable?: WritableStream<Uint8Array> | null;
|
|
12
|
+
}
|
|
13
|
+
export interface SerialOptions extends ConditioningOptions {
|
|
14
|
+
/** Web Serial port (navigator.serial.requestPort()); opened/closed per session. */
|
|
15
|
+
port?: SerialPortLike;
|
|
16
|
+
/** Pre-opened byte stream: Node serialport instance, nodeSerialSource, tests. */
|
|
17
|
+
source?: ByteSource;
|
|
18
|
+
/** Default 921_600 — the AetherOnePi ESP32 firmware rate. Only used with `port`. */
|
|
19
|
+
baudRate?: number;
|
|
20
|
+
/** Bytes discarded at session start (stale OS buffers). Default 256. */
|
|
21
|
+
warmupBytes?: number;
|
|
22
|
+
/** Written once after open — device-init quirk hook (e.g. OneRNG). Needs `port`. */
|
|
23
|
+
init?: Uint8Array;
|
|
24
|
+
/** Attribution label, e.g. 'esp32' or 'truerng'. Default 'serial'. */
|
|
25
|
+
name?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Hardware TRNG behind a serial byte stream: an ESP32 running the AetherOnePi
|
|
29
|
+
* firmware (raw 921600-baud stream of esp_fill_random bytes), a TrueRNG v3,
|
|
30
|
+
* a OneRNG (pass its init command via `init`), or any injected ByteSource.
|
|
31
|
+
* Device bytes are the raw samples — `conditioning: 'raw'` is a health-tested
|
|
32
|
+
* passthrough of exactly what the hardware emitted.
|
|
33
|
+
*/
|
|
34
|
+
export declare function serialEntropy(opts: SerialOptions): EntropyProvider;
|
|
35
|
+
//# sourceMappingURL=serial.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serial.d.ts","sourceRoot":"","sources":["../../src/providers/serial.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAiC,MAAM,4BAA4B,CAAA;AAC3F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAEnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAElD,0EAA0E;AAC1E,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,CAAA;IAC3C,QAAQ,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,CAAA;CAC7C;AAED,MAAM,WAAW,aAAc,SAAQ,mBAAmB;IACxD,mFAAmF;IACnF,IAAI,CAAC,EAAE,cAAc,CAAA;IACrB,iFAAiF;IACjF,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oFAAoF;IACpF,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,aAAa,GAAG,eAAe,CAwElE"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { iterateBytes, persistentBytes } from '../internal/byte-source.js';
|
|
2
|
+
import { sampledProvider } from '../internal/sampled.js';
|
|
3
|
+
/**
|
|
4
|
+
* Hardware TRNG behind a serial byte stream: an ESP32 running the AetherOnePi
|
|
5
|
+
* firmware (raw 921600-baud stream of esp_fill_random bytes), a TrueRNG v3,
|
|
6
|
+
* a OneRNG (pass its init command via `init`), or any injected ByteSource.
|
|
7
|
+
* Device bytes are the raw samples — `conditioning: 'raw'` is a health-tested
|
|
8
|
+
* passthrough of exactly what the hardware emitted.
|
|
9
|
+
*/
|
|
10
|
+
export function serialEntropy(opts) {
|
|
11
|
+
const { port, source, baudRate = 921_600, warmupBytes = 256, init, name = 'serial' } = opts;
|
|
12
|
+
if ((port === undefined) === (source === undefined)) {
|
|
13
|
+
throw new TypeError('serialEntropy requires exactly one of { port } or { source }');
|
|
14
|
+
}
|
|
15
|
+
if (init && !port) {
|
|
16
|
+
throw new TypeError('serialEntropy: init commands need a { port } (a raw source has no writer)');
|
|
17
|
+
}
|
|
18
|
+
async function* skipWarmup(bytes) {
|
|
19
|
+
let skipped = 0;
|
|
20
|
+
for await (const chunk of bytes) {
|
|
21
|
+
if (skipped < warmupBytes) {
|
|
22
|
+
const need = warmupBytes - skipped;
|
|
23
|
+
if (chunk.length <= need) {
|
|
24
|
+
skipped += chunk.length;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
skipped = warmupBytes;
|
|
28
|
+
yield chunk.slice(need);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
yield chunk;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// Injected sources are persistent: sessions read from one shared iterator,
|
|
36
|
+
// warmup is applied once for the source's lifetime, and closing a session
|
|
37
|
+
// never closes the source (its owner does). Ports reopen per session.
|
|
38
|
+
const sharedSource = source
|
|
39
|
+
? persistentBytes((async function* warmed() {
|
|
40
|
+
yield* skipWarmup(iterateBytes(source));
|
|
41
|
+
})())
|
|
42
|
+
: null;
|
|
43
|
+
async function* open(signal) {
|
|
44
|
+
if (port) {
|
|
45
|
+
await port.open({ baudRate });
|
|
46
|
+
try {
|
|
47
|
+
if (init && port.writable) {
|
|
48
|
+
const writer = port.writable.getWriter();
|
|
49
|
+
try {
|
|
50
|
+
await writer.write(init);
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
writer.releaseLock();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (!port.readable) {
|
|
57
|
+
throw new TypeError('serialEntropy: port has no readable stream after open()');
|
|
58
|
+
}
|
|
59
|
+
yield* skipWarmup(iterateBytes(port.readable, signal));
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
await port.close().catch(() => { });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
yield* sharedSource(signal);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return sampledProvider({
|
|
70
|
+
name,
|
|
71
|
+
kind: 'trng',
|
|
72
|
+
privacy: 'private',
|
|
73
|
+
open,
|
|
74
|
+
defaultMinEntropyPerSample: 7,
|
|
75
|
+
defaultSafetyFactor: 2,
|
|
76
|
+
}, opts);
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=serial.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serial.js","sourceRoot":"","sources":["../../src/providers/serial.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,YAAY,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAE3F,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AA0BxD;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,IAAmB;IAC/C,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAG,OAAO,EAAE,WAAW,GAAG,GAAG,EAAE,IAAI,EAAE,IAAI,GAAG,QAAQ,EAAE,GAAG,IAAI,CAAA;IAC3F,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,SAAS,CAAC,8DAA8D,CAAC,CAAA;IACrF,CAAC;IACD,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,MAAM,IAAI,SAAS,CAAC,2EAA2E,CAAC,CAAA;IAClG,CAAC;IAED,KAAK,SAAS,CAAC,CAAC,UAAU,CAAC,KAAgC;QACzD,IAAI,OAAO,GAAG,CAAC,CAAA;QACf,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;YAChC,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;gBAC1B,MAAM,IAAI,GAAG,WAAW,GAAG,OAAO,CAAA;gBAClC,IAAI,KAAK,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;oBACzB,OAAO,IAAI,KAAK,CAAC,MAAM,CAAA;oBACvB,SAAQ;gBACV,CAAC;gBACD,OAAO,GAAG,WAAW,CAAA;gBACrB,MAAM,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YACzB,CAAC;iBAAM,CAAC;gBACN,MAAM,KAAK,CAAA;YACb,CAAC;QACH,CAAC;IACH,CAAC;IAED,2EAA2E;IAC3E,0EAA0E;IAC1E,sEAAsE;IACtE,MAAM,YAAY,GAAG,MAAM;QACzB,CAAC,CAAC,eAAe,CACb,CAAC,KAAK,SAAS,CAAC,CAAC,MAAM;YACrB,KAAK,CAAC,CAAC,UAAU,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAA;QACzC,CAAC,CAAC,EAAE,CACL;QACH,CAAC,CAAC,IAAI,CAAA;IAER,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,MAAoB;QACvC,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAA;YAC7B,IAAI,CAAC;gBACH,IAAI,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;oBAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAA;oBACxC,IAAI,CAAC;wBACH,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;oBAC1B,CAAC;4BAAS,CAAC;wBACT,MAAM,CAAC,WAAW,EAAE,CAAA;oBACtB,CAAC;gBACH,CAAC;gBACD,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACnB,MAAM,IAAI,SAAS,CAAC,yDAAyD,CAAC,CAAA;gBAChF,CAAC;gBACD,KAAK,CAAC,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAA;YACxD,CAAC;oBAAS,CAAC;gBACT,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;YACpC,CAAC;QACH,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,CAAE,YAAiD,CAAC,MAAM,CAAC,CAAA;QACnE,CAAC;IACH,CAAC;IAED,OAAO,eAAe,CACpB;QACE,IAAI;QACJ,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,SAAS;QAClB,IAAI;QACJ,0BAA0B,EAAE,CAAC;QAC7B,mBAAmB,EAAE,CAAC;KACvB,EACD,IAAI,CACL,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { EntropyProvider } from '../types.js';
|
|
2
|
+
export interface SolanaBeaconOptions {
|
|
3
|
+
fetch?: typeof fetch;
|
|
4
|
+
url?: string;
|
|
5
|
+
pollIntervalMs?: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Solana latest blockhash as a PUBLIC crypto-beacon (fast ~400 ms slots).
|
|
9
|
+
* The slot leader influences block content, so treat it like every other
|
|
10
|
+
* chain beacon: auditable public randomness, never private entropy.
|
|
11
|
+
*/
|
|
12
|
+
export declare function solanaBeacon(opts?: SolanaBeaconOptions): EntropyProvider;
|
|
13
|
+
//# sourceMappingURL=solana.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solana.d.ts","sourceRoot":"","sources":["../../src/providers/solana.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAqB,MAAM,aAAa,CAAA;AAYrE,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAMD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,GAAE,mBAAwB,GAAG,eAAe,CAqE5E"}
|