Implement random
This commit is contained in:
@@ -1 +1,69 @@
|
||||
export default class Random {}
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user