From: Chris Duncan Date: Mon, 4 Aug 2025 17:29:31 +0000 (-0700) Subject: Refactor entropy to be simpler and just use overloaded constructor to create object... X-Git-Tag: v0.10.5~46^2~16 X-Git-Url: https://git.codecow.com/?a=commitdiff_plain;h=23f5cb5550cac7ab7ba5a97fe19f8d5b10ac6ffa;p=libnemo.git Refactor entropy to be simpler and just use overloaded constructor to create object synchronously. --- diff --git a/src/lib/entropy.ts b/src/lib/entropy.ts index 614b1c1..bbc8d19 100644 --- a/src/lib/entropy.ts +++ b/src/lib/entropy.ts @@ -13,7 +13,6 @@ import { bytes, hex } from './convert' * brand new source of entropy will be generated at the maximum size of 256 bits. */ export class Entropy { - static #isInternal: boolean = false static MIN: 16 = 16 static MAX: 32 = 32 static MOD: 4 = 4 @@ -24,109 +23,76 @@ export class Entropy { get bytes (): Uint8Array { return this.#bytes } get hex (): string { return bytes.toHex(this.#bytes) } - private 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. */ - static async create (): Promise + constructor () /** * Generate between 16-32 bytes of entropy. * @param {number} size - Number of bytes to generate in multiples of 4 */ - static async create (size: number): Promise - static async create (size?: number): Promise { - return new Promise(resolve => { - if (size != null) { - if (typeof size !== 'number') { - throw new TypeError(`Entropy cannot use ${typeof size} as a size`) - } - if (size < this.MIN || size > this.MAX) { - throw new RangeError(`Entropy must be ${this.MIN}-${this.MAX} bytes`) - } - if (size % this.MOD !== 0) { - throw new RangeError(`Entropy must be a multiple of ${this.MOD} bytes`) - } - } - Entropy.#isInternal = true - resolve(new this(crypto.getRandomValues(new Uint8Array(size ?? this.MAX)))) - }) - } - + constructor (size: number) /** * Import existing entropy and validate it. - * @param {string} hex - Hexadecimal string + * @param {ArrayBuffer} buffer - Byte buffer */ - static async import (hex: string): Promise + constructor (buffer: ArrayBuffer) /** * Import existing entropy and validate it. - * @param {ArrayBuffer} buffer - Byte buffer + * @param {Uint8Array} bytes - Byte array */ - static async import (buffer: ArrayBuffer): Promise + constructor (bytes: Uint8Array) /** * Import existing entropy and validate it. - * @param {Uint8Array} bytes - Byte array + * @param {string} hex - Hexadecimal string */ - static async import (bytes: Uint8Array): Promise - static async import (input: unknown): Promise { - return new Promise((resolve, reject) => { + constructor (hex: string) + constructor (input?: unknown) { - if (input instanceof ArrayBuffer) { - if (input.byteLength < this.MIN || input.byteLength > this.MAX) { - throw new Error(`Entropy must be ${this.MIN}-${this.MAX} bytes`) - } - if (input.byteLength % this.MOD !== 0) { - throw new RangeError(`Entropy must be a multiple of ${this.MOD} bytes`) - } - Entropy.#isInternal = true - resolve(new this(new Uint8Array(input))) - return + 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)) - if (input instanceof Uint8Array) { - const { buffer } = input - if (!(buffer instanceof ArrayBuffer)) { - throw new TypeError(`Entropy imported bytes must be backed by an ArrayBuffer.`) - } - this.import(buffer).then(resolve).catch(reject) - return + } 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) - if (typeof input === 'string') { - if (input.length < this.MIN * 2 || input.length > this.MAX * 2) { - throw new RangeError(`Entropy must be ${this.MIN * 2}-${this.MAX * 2} characters`) - } - if (input.length % this.MOD * 2 !== 0) { - throw new RangeError(`Entropy must be a multiple of ${this.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))) - return + } 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 (!/^[0-9a-fA-F]+$/i.test(input)) { + throw new RangeError('Entropy contains invalid hexadecimal characters') + } + this.#bytes = hex.toBytes(input) - reject(new TypeError(`Entropy cannot import ${typeof input}`)) - return - }) + } else { + throw new TypeError(`Entropy cannot import ${typeof input}`) + } } /** * Randomizes the bytes, rendering the original values generally inaccessible. */ - destroy (): boolean { - try { - crypto.getRandomValues(this.#bytes) - return true - } catch (err) { - return false + destroy (): void { + if (!this.#bytes.buffer.detached) { + this.#bytes.fill(0).buffer.transfer() } } }