| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| import { createHash, createHmac, hkdfSync, randomBytes } from 'crypto'
|
|
|
| const ANU_API = 'https://qrng.anu.edu.au/API/jsonI.php?length=16&type=hex16'
|
| const CACHE_SIZE = 256
|
| const MIN_BYTES = 32
|
| const TOLERANCE = 0.10
|
|
|
|
|
| let _cache = []
|
| let _fetching = false
|
|
|
|
|
|
|
| async function fetchANU (length = CACHE_SIZE) {
|
| const url = `https://qrng.anu.edu.au/API/jsonI.php?length=${length}&type=hex16`
|
| try {
|
| const res = await fetch(url, {
|
| signal: AbortSignal.timeout(8_000),
|
| headers: { 'Accept': 'application/json' }
|
| })
|
| if (!res.ok) throw new Error(`ANU HTTP ${res.status}`)
|
| const text = await res.text()
|
|
|
|
|
| let json
|
| try { json = JSON.parse(text) } catch { throw new Error(`ANU rate-limited or non-JSON response`) }
|
| if (!json.success || !json.data) throw new Error('ANU response missing data')
|
| return json.data.map(h => parseInt(h, 16))
|
| } catch (e) {
|
|
|
|
|
| console.error(`[quantum] ANU unreachable (${e.message}) β CSPRNG fallback (not quantum)`)
|
| return Array.from({ length }, () => parseInt(randomBytes(2).toString('hex'), 16))
|
| }
|
| }
|
|
|
| async function refillCache () {
|
| if (_fetching) return
|
| _fetching = true
|
| try {
|
| const samples = await fetchANU(CACHE_SIZE)
|
| _cache.push(...samples)
|
| } finally {
|
| _fetching = false
|
| }
|
| }
|
|
|
|
|
|
|
| function validateDistribution (uint16s) {
|
| const bytes = []
|
| for (const v of uint16s) { bytes.push((v >> 8) & 0xff, v & 0xff) }
|
| const totalBits = bytes.length * 8
|
| let ones = 0
|
| for (const b of bytes) {
|
| let x = b
|
| while (x) { ones += x & 1; x >>= 1 }
|
| }
|
| const onesRatio = ones / totalBits
|
| const passed = Math.abs(onesRatio - 0.5) <= TOLERANCE
|
| return { totalBits, ones, zeros: totalBits - ones, onesRatio, passed }
|
| }
|
|
|
|
|
|
|
| function deriveQuantumSeed (uint16s, domain = 'bob-sovereign') {
|
| const raw = Buffer.alloc(uint16s.length * 2)
|
| uint16s.forEach((v, i) => raw.writeUInt16BE(v, i * 2))
|
|
|
|
|
| const prk = createHmac('sha256', Buffer.from(domain, 'utf8')).update(raw).digest()
|
| const seed = createHmac('sha256', prk).update(Buffer.from('quantum_entropy_bob', 'utf8')).digest()
|
| return seed
|
| }
|
|
|
|
|
|
|
| function kdeExpand (seed) {
|
| return {
|
| signing_key: createHmac('sha256', seed).update('signing').digest(),
|
| worm_key: createHmac('sha256', seed).update('worm_chain').digest(),
|
| injection_key: createHmac('sha256', seed).update('ssm_injection').digest(),
|
| }
|
| }
|
|
|
|
|
|
|
| export async function getQuantumSamples (n = 16) {
|
| if (_cache.length < n) await refillCache()
|
| if (_cache.length < n) {
|
|
|
| const extra = Array.from({ length: n - _cache.length }, () =>
|
| parseInt(randomBytes(2).toString('hex'), 16))
|
| _cache.push(...extra)
|
| }
|
| return _cache.splice(0, n)
|
| }
|
|
|
|
|
|
|
| export async function getQuantumBytes (n = 32) {
|
| const samples = await getQuantumSamples(Math.ceil(n / 2))
|
| const buf = Buffer.alloc(samples.length * 2)
|
| samples.forEach((v, i) => buf.writeUInt16BE(v, i * 2))
|
| return buf.slice(0, n)
|
| }
|
|
|
|
|
|
|
| export async function getQuantumUUID () {
|
| const bytes = await getQuantumBytes(16)
|
|
|
| bytes[6] = (bytes[6] & 0x0f) | 0x40
|
| bytes[8] = (bytes[8] & 0x3f) | 0x80
|
| const hex = bytes.toString('hex')
|
| return `${hex.slice(0,8)}-${hex.slice(8,12)}-${hex.slice(12,16)}-${hex.slice(16,20)}-${hex.slice(20,32)}`
|
| }
|
|
|
|
|
|
|
| export async function getEntropyBatch (wormSealFn, domain = 'bob-sovereign') {
|
| const samples = await getQuantumSamples(CACHE_SIZE)
|
| const stats = validateDistribution(samples)
|
|
|
| if (!stats.passed) {
|
| console.warn(`[quantum] distribution failed: ratio=${stats.onesRatio.toFixed(3)} β using anyway (NISQ tolerance warning)`)
|
| }
|
|
|
| const seed = deriveQuantumSeed(samples, domain)
|
| const keys = kdeExpand(seed)
|
|
|
|
|
| let wormSeal = null
|
| if (wormSealFn) {
|
| const event = wormSealFn('QUANTUM_ENTROPY', JSON.stringify({
|
| ones_ratio: stats.onesRatio.toFixed(4),
|
| total_bits: stats.totalBits,
|
| passed: stats.passed,
|
| domain,
|
| seed_hash: createHash('sha256').update(seed).digest('hex').slice(0, 16)
|
| }), { source: 'ANU_QRNG', domain })
|
| wormSeal = event.seal
|
| }
|
|
|
| return {
|
| seed,
|
| ...keys,
|
| stats,
|
| worm_seal: wormSeal,
|
| source: 'ANU_QRNG',
|
| domain
|
| }
|
| }
|
|
|
|
|
|
|
|
|
|
|
| export async function bornCollapse (thermalMin = 0.2, thermalMax = 0.8) {
|
| const samples = await getQuantumSamples(32)
|
|
|
| const normalized = samples.map(v => v / 65535)
|
|
|
| const inWindow = normalized.filter(v => v >= thermalMin && v <= thermalMax)
|
| if (inWindow.length === 0) return null
|
|
|
| const weights = inWindow.map(v => ({ value: v, weight: 1 / inWindow.length }))
|
|
|
| const dominant = weights.sort((a, b) => b.weight - a.weight)[0]
|
| return {
|
| collapsed: dominant.value,
|
| branchCount: inWindow.length,
|
| totalBranches: samples.length,
|
| isVacuum: false
|
| }
|
| }
|
|
|
|
|
|
|
| refillCache().catch(() => {})
|
|
|