Files
2024-06-11 08:17:39 +02:00

70 lines
1.7 KiB
TypeScript

import {bytesToHex, hexToNumber} from '@noble/curves/abstract/utils'
const useless = new Uint8Array(0)
export default class Random {
private readonly state: Uint8Array;
public constructor(
state?: Uint8Array
) {
if (state === undefined) {
this.state = crypto.getRandomValues(new Uint8Array(64))
} else {
if (state.length !== 64) throw "State must have a length of 64"
this.state = state
}
}
public async random(max: bigint) : Promise<bigint> {
if (max <= 0) throw "Only works for range [0, max[, with max > 0"
const size = log2(max)
const bitsize = ((size % 8) === 0) ? size : (size - (size % 8) + 8)
while (true) {
const bytes = await this.random_bytes(bitsize)
const n = hexToNumber(bytesToHex(bytes)) >> BigInt(bitsize - size)
if (n < max) return n
}
}
async random_bytes(bitsize: number) : Promise<Uint8Array> {
const hash_size = Math.max(512, bitsize)
const state = await crypto.subtle.importKey(
"raw",
this.state,
{name: "HKDF"},
false,
["deriveBits"],
)
const buffer = await crypto.subtle.deriveBits(
{
name: "HKDF",
hash: "SHA-512",
info: useless,
salt: useless,
},
state,
hash_size,
)
const w = new Uint8Array(buffer)
for (let i = 0;i < 64;++i) this.state[i] ^= w[i]
return w.slice(0, Math.max(bitsize >> 3, 1))
}
}
function log2(n: bigint) : number {
let l = 0
while (n > 0) {
n = n >> 1n
l += 1
}
return l
}