X Tutup
const { csprngBytes } = require('./csprng-bytes') const { prngBytes } = require('./prng-bytes') const { CharSet } = require('./charSet') const BITS_PER_BYTE = 8 const { ceil, floor, log2, round } = Math const charset64 = new CharSet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_') const charset32 = new CharSet('2346789bdfghjmnpqrtBDFGHJLMNPQRT') const charset16 = new CharSet('0123456789abcdef') const charset8 = new CharSet('01234567') const charset4 = new CharSet('ATCG') const charset2 = new CharSet('01') const stringWithBytes = (bytes, bitLen, charset) => { if (bitLen <= 0) { return '' } const { bitsPerChar } = charset const count = ceil(bitLen / bitsPerChar) if (count <= 0) { return '' } const need = ceil(count * (bitsPerChar / BITS_PER_BYTE)) if (bytes.length < need) { throw new Error(`Insufficient bytes: need ${need} and got ${bytes.length}`) } const { ndxFn, charsPerChunk, chars } = charset const chunks = floor(count / charsPerChunk) const partials = count % charsPerChunk let string = '' for (let chunk = 0; chunk < chunks; chunk += 1) { for (let slice = 0; slice < charsPerChunk; slice += 1) { const ndx = ndxFn(chunk, slice, bytes) string += chars[ndx] } } for (let slice = 0; slice < partials; slice += 1) { const ndx = ndxFn(chunks, slice, bytes) string += chars[ndx] } return string } const entropyBits = (total, risk) => { if (total === 0) { return 0 } let N if (total < 1000) { N = log2(total) + log2(total - 1) } else { N = 2 * log2(total) } return (N + log2(risk)) - 1 } class Entropy { constructor(params = { bits: 128, charset: charset32 }) { if (params !== undefined) { if (!(params instanceof Object)) { throw new Error('Invalid argument for Entropy constructor: Expect params object') } if (params.bits === undefined && params.charset === undefined && params.total === undefined && params.risk === undefined && params.prng === undefined) { throw new Error('Invalid Entropy params') } if (params.bits !== undefined) { if (typeof params.bits !== 'number') { throw new Error('Invalid Entropy params: non-numeric bits') } if (params.bits < 0) { throw new Error('Invalid Entropy params: negative bits') } } if (params.total !== undefined) { if (typeof params.total !== 'number') { throw new Error('Invalid Entropy params: non-numeric total') } if (params.total < 1) { throw new Error('Invalid Entropy params: non-positive total') } } if (params.risk !== undefined) { if (typeof params.risk !== 'number') { throw new Error('Invalid Entropy params: non-numeric risk') } if (params.risk < 1) { throw new Error('Invalid Entropy params: non-positive risk') } } if (params.risk !== undefined && typeof params.risk !== 'number') { throw new Error('Invalid Entropy params: non-numeric risk') } if ((params.total !== undefined && params.risk === undefined) || (params.total === undefined && params.risk !== undefined)) { throw new Error('Invalid Entropy params: total and risk must be paired') } if (params.bits !== undefined && (params.total !== undefined || params.risk !== undefined)) { throw new Error('Invalid Entropy params: bits with total and/or risk') } } let bitLen if (params.bits) { bitLen = round(params.bits) } else if (params.total && params.risk) { bitLen = round(entropyBits(params.total, params.risk)) } else { bitLen = 128 } let charset if (params.charset instanceof CharSet) { const { charset: cs } = params charset = cs } else if ((typeof params.charset === 'string' || params.charset instanceof String)) { charset = new CharSet(params.charset) } else { charset = charset32 } this.charset = charset this.bitLen = bitLen this.prng = params.prng || false } static bits(total, risk) { return entropyBits(total, risk) } smallID(charset = this.charset) { return this.string(29, charset) } mediumID(charset = this.charset) { return this.string(69, charset) } largeID(charset = this.charset) { return this.string(99, charset) } sessionID(charset = this.charset) { return this.string(128, charset) } token(charset = this.charset) { return this.string(256, charset) } string(bitLen = this.bitLen, charset = this.charset) { const bytesNeeded = charset.bytesNeeded(bitLen) const bytes = this.prng ? prngBytes(bytesNeeded) : csprngBytes(bytesNeeded) return this.stringWithBytes(bytes, bitLen, charset) } stringWithBytes( bytes, bitLen = this.bitLen, charset = this.charset ) { return stringWithBytes(bytes, bitLen, charset) } bytesNeeded(bitLen = this.bitLen, charset = this.charset) { return charset.bytesNeeded(bitLen) } chars() { return this.charset.chars } bits() { return this.bitLen } use(charset) { if (!(charset instanceof CharSet)) { throw new Error('Invalid CharSet') } this.charset = charset } useChars(chars) { if (!(typeof chars === 'string' || chars instanceof String)) { throw new Error('Invalid chars: Must be string') } this.use(new CharSet(chars)) } } module.exports = { CharSet, Entropy, charset2, charset4, charset8, charset16, charset32, charset64 }
X Tutup