+++ /dev/null
-//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
-//! SPDX-License-Identifier: GPL-3.0-or-later
-
-import { bytes, hex } from './convert'
-
-/**
-* Represents a cryptographically strong source of entropy suitable for use in
-* BIP-39 mnemonic phrase generation and consequently BIP-44 key derivation.
-*
-* The constructor will accept one of several different data types under certain
-* constraints. If the constraints are not met, an error will be thrown. If no
-* value, or the equivalent of no value, is passed to the constructor, then a
-* brand new source of entropy will be generated at the maximum size of 256 bits.
-*/
-export class Entropy {
- static #MIN: 16 = 16
- static #MAX: 32 = 32
- static #MOD: 4 = 4
- #bigint?: bigint
- #bytes: Uint8Array<ArrayBuffer>
-
- get bigint (): bigint {
- if (this.#bigint == null) {
- this.#bigint = 0n
- for (let i = 0; i < this.#bytes.byteLength; i++) {
- this.#bigint <<= 8n
- this.#bigint |= BigInt(this.#bytes[i])
- }
- }
- return this.#bigint
- }
- get bits (): string { return bytes.toBin(this.#bytes) }
- get buffer (): ArrayBuffer { return this.#bytes.buffer }
- get byteLength (): number { return this.#bytes.byteLength }
- get bytes (): Uint8Array<ArrayBuffer> { return this.#bytes }
- get hex (): string { return bytes.toHex(this.#bytes) }
-
- /**
- * Generate 256 bits of entropy.
- */
- constructor ()
- /**
- * Generate between 16-32 bytes of entropy.
- * @param {number} size - Number of bytes to generate in multiples of 4
- */
- constructor (size: number)
- /**
- * Import existing entropy and validate it.
- * @param {ArrayBuffer} buffer - Byte buffer
- */
- constructor (buffer: ArrayBuffer)
- /**
- * Import existing entropy and validate it.
- * @param {Uint8Array<ArrayBuffer>} bytes - Byte array
- */
- constructor (bytes: Uint8Array<ArrayBuffer>)
- /**
- * Import existing entropy and validate it.
- * @param {string} hex - Hexadecimal string
- */
- constructor (hex: string)
- constructor (input?: unknown)
- constructor (input?: unknown) {
-
- if (input == null) {
- this.#bytes = crypto.getRandomValues(new Uint8Array(Entropy.#MAX))
-
- } else if (typeof input === 'number') {
- if (input < Entropy.#MIN || input > Entropy.#MAX) {
- throw new RangeError(`Entropy must be ${Entropy.#MIN}-${Entropy.#MAX} bytes`)
- }
- if (input % Entropy.#MOD !== 0) {
- throw new RangeError(`Entropy must be a multiple of ${Entropy.#MOD} bytes`)
- }
- this.#bytes = crypto.getRandomValues(new Uint8Array(input))
-
- } else if (input instanceof ArrayBuffer || input instanceof Uint8Array) {
- if (input.byteLength < Entropy.#MIN || input.byteLength > Entropy.#MAX) {
- throw new Error(`Entropy must be ${Entropy.#MIN}-${Entropy.#MAX} bytes`)
- }
- if (input.byteLength % Entropy.#MOD !== 0) {
- throw new RangeError(`Entropy must be a multiple of ${Entropy.#MOD} bytes`)
- }
- this.#bytes = new Uint8Array(input)
-
- } else if (typeof input === 'string') {
- if (input.length < Entropy.#MIN * 2 || input.length > Entropy.#MAX * 2) {
- throw new RangeError(`Entropy must be ${Entropy.#MIN * 2}-${Entropy.#MAX * 2} characters`)
- }
- if (input.length % Entropy.#MOD * 2 !== 0) {
- throw new RangeError(`Entropy must be a multiple of ${Entropy.#MOD * 2} characters`)
- }
- if (!/^[A-F0-9]+$/i.test(input)) {
- throw new RangeError('Entropy contains invalid hexadecimal characters')
- }
- this.#bytes = hex.toBytes(input)
-
- } else {
- throw new TypeError(`Entropy cannot import ${typeof input}`)
- }
- }
-
- /**
- * Zeros the bytes and transfers the buffer, rendering the values inaccessible.
- */
- destroy (): void {
- if (!this.#bytes.buffer.detached) {
- this.#bytes.fill(0).buffer.transfer()
- }
- }
-}