* brand new source of entropy will be generated at the maximum size of 256 bits.
*/
export class Entropy {
- #bits: string
- #buffer: ArrayBuffer
+ static #isInternal: boolean = false
#bytes: Uint8Array
- #hex: string
- get bits (): string { return this.#bits }
- get buffer (): ArrayBuffer { return this.#buffer }
+ get bits (): string { return bytes.toBin(this.#bytes) }
+ get buffer (): ArrayBuffer { return this.#bytes.buffer }
get bytes (): Uint8Array { return this.#bytes }
- get hex (): string { return this.#hex }
+ get hex (): string { return bytes.toHex(this.#bytes) }
+
+ constructor (bytes: Uint8Array) {
+ if (!Entropy.#isInternal) {
+ throw new Error(`Entropy cannot be instantiated directly. Use 'await Entropy.create()' instead.`)
+ }
+ Entropy.#isInternal = false
+ this.#bytes = bytes
+ }
/**
* Generate 256 bits of entropy.
*/
- constructor ()
+ static async create (): Promise<Entropy>
/**
* Generate between 16-32 bytes of entropy.
* @param {number} size - Number of bytes to generate in multiples of 4
*/
- constructor (size: number)
+ static async create (size: number): Promise<Entropy>
+ static async create (size?: number): Promise<Entropy> {
+ return new Promise(resolve => {
+ if (size != null) {
+ if (typeof size !== 'number') {
+ throw new TypeError(`Entropy cannot use ${typeof size} as a size`)
+ }
+ if (size < MIN || size > MAX) {
+ throw new RangeError(`Entropy must be ${MIN}-${MAX} bytes`)
+ }
+ if (size % MOD !== 0) {
+ throw new RangeError(`Entropy must be a multiple of ${MOD} bytes`)
+ }
+ }
+ Entropy.#isInternal = true
+ resolve(new this(crypto.getRandomValues(new Uint8Array(size ?? MAX))))
+ })
+ }
+
/**
* Import existing entropy and validate it.
* @param {string} hex - Hexadecimal string
*/
- constructor (hex: string)
+ static async import (hex: string): Promise<Entropy>
/**
* Import existing entropy and validate it.
* @param {ArrayBuffer} buffer - Byte buffer
*/
- constructor (buffer: ArrayBuffer)
+ static async import (buffer: ArrayBuffer): Promise<Entropy>
/**
* Import existing entropy and validate it.
* @param {Uint8Array} bytes - Byte array
*/
- constructor (bytes: Uint8Array)
- constructor (input?: number | string | ArrayBuffer | Uint8Array) {
- if (typeof input === 'number' && input > 0) {
- if (input < MIN || input > MAX) {
- throw new RangeError(`Entropy must be ${MIN}-${MAX} bytes`)
- }
- if (input % MOD !== 0) {
- throw new RangeError(`Entropy must be a multiple of ${MOD} bytes`)
+ static async import (bytes: Uint8Array): Promise<Entropy>
+ static async import (input: string | ArrayBuffer | Uint8Array): Promise<Entropy> {
+ return new Promise((resolve, reject) => {
+ if (typeof input === 'string') {
+ if (input.length < MIN * 2 || input.length > MAX * 2) {
+ throw new RangeError(`Entropy must be ${MIN * 2}-${MAX * 2} characters`)
+ }
+ if (input.length % MOD * 2 !== 0) {
+ throw new RangeError(`Entropy must be a multiple of ${MOD * 2} characters`)
+ }
+ if (!/^[0-9a-fA-F]+$/i.test(input)) {
+ throw new RangeError('Entropy contains invalid hexadecimal characters')
+ }
+ Entropy.#isInternal = true
+ resolve(new this(hex.toBytes(input)))
}
- this.#bytes = crypto.getRandomValues(new Uint8Array(input))
- this.#hex = bytes.toHex(this.#bytes)
- this.#bits = hex.toBin(this.#hex)
- this.#buffer = this.#bytes.buffer
- return
- }
- if (typeof input === 'string' && input.length > 0) {
- if (input.length < MIN * 2 || input.length > MAX * 2) {
- throw new RangeError(`Entropy must be ${MIN * 2}-${MAX * 2} characters`)
- }
- if (input.length % MOD * 2 !== 0) {
- throw new RangeError(`Entropy must be a multiple of ${MOD * 2} characters`)
+ if (input instanceof ArrayBuffer) {
+ if (input.byteLength < MIN || input.byteLength > MAX) {
+ throw new Error(`Entropy must be ${MIN}-${MAX} bytes`)
+ }
+ if (input.byteLength % MOD !== 0) {
+ throw new RangeError(`Entropy must be a multiple of ${MOD} bytes`)
+ }
+ Entropy.#isInternal = true
+ resolve(new this(new Uint8Array(input)))
}
- this.#hex = input
- if (!/^[0-9a-fA-F]+$/i.test(this.#hex)) {
- throw new RangeError('Entropy contains invalid hexadecimal characters')
- }
- this.#bytes = hex.toBytes(this.#hex)
- this.#bits = hex.toBin(this.#hex)
- this.#buffer = this.#bytes.buffer
- return
- }
-
- if (input instanceof ArrayBuffer && input.byteLength > 0) {
- if (input.byteLength < MIN || input.byteLength > MAX) {
- throw new Error(`Entropy must be ${MIN}-${MAX} bytes`)
- }
- if (input.byteLength % MOD !== 0) {
- throw new RangeError(`Entropy must be a multiple of ${MOD} bytes`)
- }
- this.#buffer = input
- this.#bytes = new Uint8Array(this.#buffer)
- this.#bits = bytes.toBin(this.#bytes)
- this.#hex = bytes.toHex(this.#bytes)
- return
- }
- if (input instanceof Uint8Array && input.length > 0) {
- if (input.length < MIN || input.length > MAX) {
- throw new Error(`Entropy must be ${MIN}-${MAX} bytes`)
+ if (input instanceof Uint8Array) {
+ if (input.length < MIN || input.length > MAX) {
+ throw new Error(`Entropy must be ${MIN}-${MAX} bytes`)
+ }
+ if (input.length % MOD !== 0) {
+ throw new RangeError(`Entropy must be a multiple of ${MOD} bytes`)
+ }
+ Entropy.#isInternal = true
+ resolve(new this(input))
}
- if (input.length % MOD !== 0) {
- throw new RangeError(`Entropy must be a multiple of ${MOD} bytes`)
- }
- this.#bytes = input
- this.#bits = bytes.toBin(this.#bytes)
- this.#buffer = this.#bytes.buffer
- this.#hex = bytes.toHex(this.#bytes)
- return
- }
- this.#bytes = crypto.getRandomValues(new Uint8Array(MAX))
- this.#hex = bytes.toHex(this.#bytes)
- this.#bits = hex.toBin(this.#hex)
- this.#buffer = this.#bytes.buffer
+ reject(new TypeError(`Entropy cannot import ${typeof input}`))
+ })
}
}