UNPKG

cripta

Version:

A layer of encryption a little too judicious

107 lines (81 loc) 2.96 kB
import { Envir, toInt } from 't0n' import { sha256 } from './hash' import type { ConfigOptions } from './types' export class Config { entropy: number = 4 baseAlphabet: string = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_' key?: Buffer seed?: number alphabet?: string static #sortedCache: Record<string, any> = {} constructor(opts?: ConfigOptions) { this.setKey(opts?.key || Envir.get<string>('APP_KEY') || Envir.get<string>('KEY')) .setSeed(opts?.seed) .setEntropy(opts?.entropy) } setKey(key?: string): this { if (typeof key !== 'string') return this key = key?.trim() if (!key) return this if (key.indexOf('base64:') > -1) { this.key = sha256(Buffer.from(key.substring(7), 'base64'), 'buffer') return this } this.key = sha256(key, 'buffer') return this } setSeed(value: any): this { if (!value) return this const numValue = typeof value === 'number' ? Math.abs(value) : toInt(value) if (!this.seed || this.seed !== numValue) this.alphabet = this.sortSeed(this.baseAlphabet, numValue) this.seed = numValue return this } setEntropy(value: any): this { if (value === null || value === undefined) return this this.entropy = typeof value === 'number' ? Math.abs(value) : toInt(value) return this } getInt(key: string): number { const value = (this as any)[key] return typeof value === 'number' ? value : toInt(value) } sortSeed<T extends string | string[]>(data: T, seed: number | string | null = null): T { if (!seed) return data const numericSeed = typeof seed === 'string' ? this.hashStringToNumber(seed) : seed const dataString = Array.isArray(data) ? JSON.stringify(data) : data as string const key = `${this.hashStringToNumber(dataString)}-${numericSeed}` if (Config.#sortedCache[key]) return Config.#sortedCache[key] as T const isArray = Array.isArray(data) const items = isArray ? [...data] : this.#splitUnicodeString(data) const rng = this.createSeededRNG(numericSeed) const randomized = items .map((item, index) => ({ item, sortKey: rng() })) .sort((a, b) => a.sortKey - b.sortKey) .map(x => x.item) const result = (isArray ? randomized : randomized.join('')) as T Config.#sortedCache[key] = result return result } #splitUnicodeString(str: string): string[] { return str.split(/(?!^)(?=.)/u).filter(Boolean) } createSeededRNG(seed: number): () => number { return function() { seed |= 0 seed = seed + 0x6D2B79F5 | 0 let t = Math.imul(seed ^ seed >>> 15, 1 | seed) t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t return ((t ^ t >>> 14) >>> 0) / 4294967296 } } hashStringToNumber(str: string): number { let hash = 5381 for (let i = 0; i < str.length; i++) hash = (hash * 33) ^ str.charCodeAt(i) return hash >>> 0 } }