UNPKG

@synotech/crypto

Version:

Comprehensive cryptography library with Web3 integration, supporting symmetric/asymmetric encryption, digital signatures, JWT tokens, blockchain address generation, and secure key management

1,000 lines (994 loc) 32.8 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var crypto = require('crypto'); var jwt = require('jsonwebtoken'); var OTPAuth = require('otpauth'); var uniqueNamesGenerator = require('unique-names-generator'); function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var jwt__namespace = /*#__PURE__*/_interopNamespace(jwt); var OTPAuth__namespace = /*#__PURE__*/_interopNamespace(OTPAuth); var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var SecureEntropyPool = class { static { __name(this, "SecureEntropyPool"); } constructor() { if (!crypto.randomBytes) { throw new Error("Node.js crypto module not available"); } } getSecureRandomBytes(length) { return crypto.randomBytes(length); } generateEntropyPool(poolSize = 100, byteLength = 32) { const entropyPool = []; for (let i = 0; i < poolSize; i++) { const randomBytesBuffer = this.getSecureRandomBytes(byteLength); const hexString = randomBytesBuffer.toString("hex"); entropyPool.push(hexString); } return entropyPool; } generate(options = {}) { const config = { poolSize: 100, byteLength: 32, ...options }; return this.generateEntropyPool(config.poolSize, config.byteLength); } generateKeyPool(count, keyLength) { const keys = []; for (let i = 0; i < count; i++) { const randomBytesBuffer = this.getSecureRandomBytes(keyLength); const hexString = randomBytesBuffer.toString("hex"); keys.push(hexString); } return keys; } }; var Cryptography = class { static { __name(this, "Cryptography"); } ALG; KEYS; keys; entropyPool; lowerCase = "abcdefghijklmnopqrstuvwxyz"; upperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; numbers = "1234567890"; special = "`~!@#$%^&*()-=_+[]{}|;\\':\",./<>?"; hex = "123456789ABCDEF"; /** * Initializes a new instance of the Cryptography class. */ constructor(options = {}) { const { encryptKey, encryptKeySingle } = options; const aesEncryptionKey = encryptKey || process.env.CRYPTO_AES_ENCRYPTION_KEY; const hmacSigningKey = encryptKeySingle || process.env.CRYPTO_HMAC_SIGNING_KEY; if (!aesEncryptionKey || typeof aesEncryptionKey !== "string" || aesEncryptionKey.trim().length === 0) { throw new Error( "AES encryption key is required. Provide via encryptKey option or CRYPTO_AES_ENCRYPTION_KEY environment variable" ); } if (!hmacSigningKey || typeof hmacSigningKey !== "string" || hmacSigningKey.trim().length === 0) { throw new Error( "HMAC signing key is required. Provide via encryptKeySingle option or CRYPTO_HMAC_SIGNING_KEY environment variable" ); } if (aesEncryptionKey.length !== 64) { throw new Error("AES encryption key must be exactly 64 hex characters long for AES-256"); } if (!/^[0-9a-fA-F]{64}$/.test(aesEncryptionKey)) { throw new Error("AES encryption key must be a valid 64-character hex string"); } if (hmacSigningKey.length !== 64) { throw new Error("HMAC signing key must be exactly 64 hex characters long"); } if (!/^[0-9a-fA-F]{64}$/.test(hmacSigningKey)) { throw new Error("HMAC signing key must be a valid 64-character hex string"); } this.entropyPool = new SecureEntropyPool(); this.keys = this.entropyPool.generateKeyPool(3, 16); this.KEYS = { encryptKey: aesEncryptionKey, encryptKeySingle: hmacSigningKey }; this.ALG = { /** * GCM is an authenticated encryption mode that * not only provides confidentiality but also * provides integrity in a secured way. */ NODE_CIPHER: "aes-256-gcm", /** * 128 bit auth tag is recommended for GCM. */ AUTH_TAG_BYTE_LEN: 16, /** * NIST recommends 96 bits or 12 bytes IV for GCM * to promote interoperability, efficiency, and * simplicity of design. */ IV_BYTE_LEN: 12, /** * Note: 256 (in ALG name) is key size. * NODE size for AES is always 128. */ KEY_BYTE_LEN: 32, /** * To prevent rainbow table attacks. */ SALT_BYTE_LEN: 16 }; } randomizer() { return this.entropyPool.getSecureRandomBytes(1).readUInt8(0) / 255; } /** * @method random * The Random module class provides methods for generating random strings. * * @example * cryptography.random({ length: 20, useLowerCase: true, useUpperCase: true, useNumbers: true, useSpecial: true, useHex: false }) // returns *ajz:74,*ak0 * * // Decent Passwords - Good for nothing really, public accounts and other non-critical things. * cryptography.get('decent_pw') // returns rXjdx36oro * * // Strong Passwords - Robust enough to keep your web hosting account secure. * cryptography.get('strong_pw') // returns i=SQ_qa3W[<RxoM * * // Fort Knox Passwords - Secure enough for almost anything, like root or administrator passwords. * cryptography.get('ft_knox_pw') // returns P}U%H\OOYAYb;wc"3hgI,3Lz[gd-z] * * // Encryption Keys - Can be used for any other 256-bit key requirement. * cryptography.get('ci_key') // returns CeXHpM3nDgzdv0o3AkMCs3OuxzepLGW8 * * // 160-bit WPA Key * cryptography.get('160_wpa') // returns oHI#gR8z#h7BS>cZ!zH( * * // 504-bit WPA Key * cryptography.get('504_wpa') // returns <os[g`s}u06jqt"Ea]t11,HsI[UipHD)%F";:9RhJ@kTU8GknLpMAXtoCzsJjT` * * // 64-bit WEP Keys * cryptography.get('64_wep') // returns 8911B * * // 128-bit WEP Keys * cryptography.get('128_wep') // returns 9F4E4F933BCCC * * // 152-bit WEP Keys * cryptography.get('152_wep') // returns 695E1EE96E483961 * * // 256-bit WEP Keys * cryptography.get('256_wep') // returns AC7E866246BA6B71BF5D88A6861AB * */ random(options) { const { length = 32, useLowerCase = true, useUpperCase = true, useNumbers = true, useSpecial = false, useHex = false } = options; let chars = ""; let key = ""; if (useLowerCase) chars += this.lowerCase; if (useUpperCase) chars += this.upperCase; if (useNumbers) chars += this.numbers; if (useSpecial) chars += this.special; if (useHex) chars += this.hex; if (chars.length === 0) { throw new Error("At least one character set must be enabled"); } for (let i = 0; i < length; i++) { const randomIndex = Math.floor(this.randomizer() * chars.length); const safeIndex = Math.min(randomIndex, chars.length - 1); key += chars[safeIndex]; } return key; } /** * * @method get * @param strength - The strength of the key to generate. decent_pw|strong_pw|ft_knox_pw|ci_key|160_wpa|504_wpa|64_wep|128_wep|152_wep|256_wep */ get(strength) { switch (strength) { case "decent_pw": return this.random({ length: 10, useLowerCase: true, useUpperCase: true, useNumbers: true, useSpecial: false, useHex: false }); case "strong_pw": return this.random({ length: 15, useLowerCase: true, useUpperCase: true, useNumbers: true, useSpecial: true, useHex: false }); case "ft_knox_pw": return this.random({ length: 30, useLowerCase: true, useUpperCase: true, useNumbers: true, useSpecial: true, useHex: false }); case "ci_key": return this.random({ length: 32, useLowerCase: true, useUpperCase: true, useNumbers: true, useSpecial: false, useHex: false }); case "160_wpa": return this.random({ length: 20, useLowerCase: true, useUpperCase: true, useNumbers: true, useSpecial: true, useHex: false }); case "504_wpa": return this.random({ length: 63, useLowerCase: true, useUpperCase: true, useNumbers: true, useSpecial: true, useHex: false }); case "64_wep": return this.random({ length: 5, useLowerCase: false, useUpperCase: false, useNumbers: false, useSpecial: false, useHex: true }); case "128_wep": return this.random({ length: 13, useLowerCase: false, useUpperCase: false, useNumbers: false, useSpecial: false, useHex: true }); case "152_wep": return this.random({ length: 16, useLowerCase: false, useUpperCase: false, useNumbers: false, useSpecial: false, useHex: true }); case "256_wep": return this.random({ length: 29, useLowerCase: false, useUpperCase: false, useNumbers: false, useSpecial: false, useHex: true }); default: throw new Error(`No such strength`); } } /** * @method salt * Generates a random salt value. * @returns The generated salt value as a hexadecimal string. * * @example * const salt = cryptography.salt() * console.log(salt) * // Output: * // 5eb63bbbe01eeed093cb22bb8f5acdc3 */ salt() { return crypto.randomBytes(this.ALG.SALT_BYTE_LEN).toString("hex"); } /** * @method iv * Generates a random initialization vector (IV). * @returns The generated IV as a Buffer. */ iv() { return crypto.randomBytes(this.ALG.AUTH_TAG_BYTE_LEN); } /** * @method signature * Generates a signature using the encryption keys. * @returns The generated signature as a string. * * @example * const signature = cryptography.signature() * console.log(signature) * // Output: * // 6a3a4b5c6d7e8f9a */ signature() { return this.encrypt(this.getKeys(2).join(" ")); } /** * @method signatureVerify * Verifies a signature against the encryption keys. * @param signature - The signature to verify. * @returns True if the signature is valid, false otherwise. * @throws An error if the signature contains unknown identifiers. * * @example * const signature = '6a3a4b5c6d7e8f9a' * const isValid = cryptography.signatureVerify(signature) * console.log(isValid) * // Output: * // true */ signatureVerify(signature) { const signatures = this.decrypt(signature).split(" "); if (signatures.every((value) => this.keys.includes(value))) { return true; } else { throw `Unknown identifier`; } } /** * @method address * Generates a random crypto address. * @returns The generated address as a string. * @throws An error if an error occurs during address generation. * * @example * const address = cryptography.address() * console.log(address) * // Output: * // KGRHCVlAmmEnnlKh6wVX9TiJ6YGI7FCl */ address(prefix = "KGR") { try { const address = this.random({ length: this.ALG.KEY_BYTE_LEN - 3 }); return `${prefix}${address}`; } catch (error) { throw error; } } /** * @method getKeys * Generates random keys from the provided key array. * @param count - The number of keys to generate. Defaults to 12. * @returns An array of randomly generated keys. */ getKeys = /* @__PURE__ */ __name((count) => { count = count ?? 12; const random = []; const availableIndices = Array.from({ length: this.keys.length }, (_, i) => i); for (let i = 0; i < count && availableIndices.length > 0; i++) { const randomIndex = Math.floor(this.entropyPool.getSecureRandomBytes(1).readUInt8(0) / 255 * availableIndices.length); const keyIndex = availableIndices.splice(randomIndex, 1)[0]; const randomWord = this.keys[keyIndex]; if (randomWord && typeof randomWord === "string" && randomWord.length > 0) { random.push(randomWord); } else { i--; } } return random; }, "getKeys"); /** * @method encryptSingle * Encrypts data one way using an encryption key. * @param data - The data to encrypt. * @returns The encrypted data as a base64-encoded string. * * @example * const crypto = new Cryptography(['key1', 'key2', 'key3'], 'encryptKey', 'encryptKeySingle') * const data = 'Hello, World!' * const encryptedData = cryptography.encryptSingle(data) * console.log(encryptedData) * // Output: * // 5eb63bbbe01eeed093cb22bb8f5acdc3 */ encryptSingle(data) { try { const signature = crypto.createHmac(`sha256`, this.KEYS.encryptKeySingle).update(data).digest(`hex`); const b64Buffer = Buffer.from(signature); return b64Buffer.toString(`base64`); } catch (error) { return Buffer.alloc(16).toString("base64"); } } /** * @method encrypt * Encrypts text two way using the encryption keys. * @param text - The text to encrypt. * @returns The encrypted text as a string. * @throws An error if an error occurs during encryption. * * @example * const text = 'Hello, World!' * const encryptedText = cryptography.encrypt(text) * console.log(encryptedText) * // Output: * // 6a3a4b5c6d7e8f9a:6a3a4b5c6d7e8f9a:6a3a4b5c6d7e8f9a */ encrypt(text) { try { const textToEncrypt = String(text); const iv = crypto.randomBytes(this.ALG.AUTH_TAG_BYTE_LEN); const keyBuffer = Buffer.from(this.KEYS.encryptKey, "hex"); const cipher = crypto.createCipheriv(this.ALG.NODE_CIPHER, keyBuffer, iv); let encrypted = cipher.update(textToEncrypt, "utf8", "hex"); encrypted += cipher.final("hex"); const tag = cipher.getAuthTag(); return `${iv.toString("hex")}:${encrypted}:${tag.toString("hex")}`; } catch (error) { throw error; } } /** * @method decrypt * Decrypts encrypted text using the encryption keys. * @param encryptedText - The encrypted text to decrypt. * @returns The decrypted text as a string. * @throws An error if an error occurs during decryption. * * @example * const encryptedText = '6a3a4b5c6d7e8f9a:6a3a4b5c6d7e8f9a:6a3a4b5c6d7e8f9a' * const decryptedText = cryptography.decrypt(encryptedText) * console.log(decryptedText) * // Output: * // Hello, World! */ decrypt(encryptedText) { const [ivHex, encryptedHex, tagHex] = encryptedText.split(":"); const iv = Buffer.from(ivHex, "hex"); const encrypted = Buffer.from(encryptedHex, "hex"); const tag = Buffer.from(tagHex, "hex"); const keyBuffer = Buffer.from(this.KEYS.encryptKey, "hex"); const decipher = crypto.createDecipheriv(this.ALG.NODE_CIPHER, keyBuffer, iv); decipher.setAuthTag(tag); let decrypted = decipher.update(encrypted, void 0, "utf8"); decrypted += decipher.final("utf8"); return decrypted; } /** * @method password * Generates a password hash using the provided password and salt. * @param password - The password to hash. * @param salt - The salt value to use for hashing. * @returns The hashed password as a hexadecimal string. * @throws An error if an error occurs during password hashing. * * @example * const password = 'myPassword' * const salt = cryptography.salt() * const hashedPassword = cryptography.password(password, salt) * console.log(hashedPassword) * // Output: * // 5ebe2294ecd0e0f08eab7690d2a6ee69 */ password(password, salt) { try { const pbkdf = crypto.pbkdf2Sync(password, salt, 1e4, 256, `sha256`); return pbkdf.toString("hex"); } catch (error) { throw error; } } /** * @method jwtIssue * Issues a JSON Web Token (JWT) with the provided payload and expiry. * @param payload - The payload to include in the JWT. * @param expiry - The expiry time for the JWT. * @returns The issued JWT as a string. * @throws An error if an error occurs during JWT issuance. * * @example * const payload = { * sub: '1234567890', * name: 'John Doe', * iat: 1516239022 * } * * const expiry = '1h' * const token = cryptography.jwtIssue(payload, expiry) * console.log(token) * // Output: * // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c */ jwtIssue(payload = {}, expiry) { try { return jwt__namespace.sign( { ...payload }, this.KEYS.encryptKeySingle, { expiresIn: expiry } ); } catch (error) { throw error; } } /** * @method jwtVerify * Verifies a JSON Web Token (JWT) and returns the decoded payload. * @param token - The JWT to verify. * @returns The decoded payload if the JWT is valid. * @throws An error if the JWT is invalid or an error occurs during verification. * * @example * const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' * const payload = cryptography.jwtVerify(token) * console.log(payload) * // Output: * // { * // sub: '1234567890', * // name: 'John Doe', * // iat: 1516239022 * // } */ jwtVerify(token) { try { return jwt__namespace.verify(token, this.KEYS.encryptKeySingle); } catch (error) { throw error; } } /** * @method publicKey * Generates a private key for asymmetric encryption. * @returns The generated private key as a PEM-encoded string. * @throws An error if an error occurs during private key generation. * * @example * const privateKey = cryptography.privateKey() * console.log(privateKey) * // Output: * // -----BEGIN PRIVATE KEY----- * // MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBA4GCAWqjggFMMIIB * // ... * // -----END PRIVATE KEY----- */ privateKey() { try { const { privateKey } = crypto.generateKeyPairSync(`ec`, { namedCurve: "secp521r1" }); return privateKey.export({ type: "pkcs8", format: "pem" }); } catch (error) { throw error; } } /** * @method publicKey * * Generates a public key from the provided private key. * @param privateKey - The private key to generate the public key from. * @returns The generated public key as a PEM-encoded string. * * @example * const privateKey = `-----BEGIN PRIVATE KEY----- * MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDZ1Ck6vJQK0J5T * ... * -----END PRIVATE KEY-----` * * const publicKey = cryptography.publicKey(privateKey) * console.log(publicKey) * // Output: * // -----BEGIN PUBLIC KEY----- * // MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2dQpOryUCtCeUz8vZ6zB * // ... * // -----END PUBLIC KEY----- */ publicKey(privateKey) { const publicKey = crypto.createPublicKey(privateKey); return publicKey.export({ type: "spki", format: "pem" }); } /** * @method publicKeyVerify * Verifies the authenticity of a public key using the provided private and public keys. * @param privateKey - The private key used to sign the data. * @param publicKey - The public key used to verify the signature. * @returns True if the public key is authentic, false otherwise. * @throws An error if the public key fails to authenticate. * * @example * const privateKey = `-----BEGIN PRIVATE KEY----- * MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDZ1Ck6vJQK0J5T * ... * -----END PRIVATE KEY-----` * const publicKey = `-----BEGIN PUBLIC KEY----- * MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2dQpOryUCtCeUz8vZ6zB * ... * -----END PUBLIC KEY-----` * const isAuthentic = cryptography.publicKeyVerify({ privateKey, publicKey }) * console.log(isAuthentic) * // Output: * // true */ publicKeyVerify({ privateKey, publicKey }) { try { const data = this.KEYS.encryptKey; const sign2 = crypto.createSign(`RSA-SHA256`); sign2.update(data); const signature = sign2.sign(privateKey, `base64`); const verify2 = crypto.createVerify(`RSA-SHA256`); verify2.update(data); const isValid = verify2.verify(publicKey, signature, `base64`); if (isValid) { return true; } else { throw `Failed to authenticate the public key`; } } catch (error) { throw error; } } /** * @method isBase64Encoded * Checks if a string is base64 encoded. * @param string - The string to check. * @returns True if the string is base64 encoded, false otherwise. * * @example * const encodedString = 'SGVsbG8sIFdvcmxkIQ==' * const isEncoded = cryptography.isBase64Encoded(encodedString) * console.log(isEncoded) * // Output: * // true */ isBase64Encoded(string) { try { if (string === "") return true; if (!/^[A-Za-z0-9+/]*={0,2}$/.test(string)) return false; const decoded = Buffer.from(string, "base64").toString("base64"); return decoded === string; } catch { return false; } } /** * @method base64Encode * Encodes data as base64. * @param data - The data to encode. * @returns The base64-encoded string. * * @example * const data = 'Hello, World!' * const encodedData = cryptography.base64Encode(data) * console.log(encodedData) * // Output: * // SGVsbG8sIFdvcmxkIQ== */ base64Encode(data) { return Buffer.from(data).toString("base64"); } /** * @method base64Decode * Decodes a base64-encoded string. * @param encodedString - The base64-encoded string to decode. * @returns The decoded string. * * @example * const encodedString = 'SGVsbG8sIFdvcmxkIQ==' * const decodedString = cryptography.base64Decode(encodedString) * console.log(decodedString) * // Output: * // Hello, World! */ base64Decode(encodedString) { if (!this.isBase64Encoded(encodedString)) { throw new Error("Invalid base64 string"); } return Buffer.from(encodedString, "base64").toString("utf-8"); } /** * @method apiKey * Generates a secure API key with HMAC-based authentication. * @param options - Configuration options for API key generation. * @param options.prefix - The 3-letter prefix for the API key. Defaults to 'SYN'. * @param options.length - The length of the key portion (excluding prefix and separator). Defaults to 48. * @returns The generated API key in format: prefix_keyHash * * @example * const apiKey = cryptography.apiKey() * console.log(apiKey) * // Output: * // SYN_a1b2c3d4e5f6... * * const customApiKey = cryptography.apiKey({ prefix: 'DEV', length: 32 }) * console.log(customApiKey) * // Output: * // SYN_x1y2z3a4b5c6... */ apiKey(options = {}) { if ("prefix" in options && (options.prefix === void 0 || options.prefix === null || options.prefix === "" || typeof options.prefix !== "string" || options.prefix.trim().length === 0)) { throw new Error("Prefix must be a non-empty string"); } const { prefix = "SYN", length = 48 } = options; if (prefix.length !== 3) { throw new Error("Prefix must be exactly 3 characters long"); } if (!/^[A-Z]{3}$/.test(prefix)) { throw new Error("Prefix must contain only uppercase letters"); } if (!Number.isInteger(length) || length < 16 || length > 128) { throw new Error("Length must be an integer between 16 and 128"); } const randomData = this.random({ length: 16, useLowerCase: true, useUpperCase: true, useNumbers: true, useSpecial: false, useHex: false }); const basePayload = `${prefix}_${randomData}`; let signature = crypto.createHmac("sha256", this.KEYS.encryptKeySingle).update(basePayload).digest("hex"); while (signature.length < length) { const additionalSignature = crypto.createHmac("sha256", this.KEYS.encryptKeySingle).update(signature + basePayload).digest("hex"); signature += additionalSignature; } return `${prefix}_${signature.substring(0, length)}`; } /** * @method apiKeyVerify * Verifies the authenticity of an API key using HMAC validation. * @param apiKey - The API key to verify. * @param options - Optional configuration for verification. * @param options.prefix - Expected prefix to validate against. If not provided, extracts from the key. * @returns True if the API key is valid, false otherwise. * * @example * const apiKey = cryptography.apiKey({ prefix: 'SYN' }) * const isValid = cryptography.apiKeyVerify(apiKey) * console.log(isValid) * // Output: * // true * * const isValidWithPrefix = cryptography.apiKeyVerify(apiKey, { prefix: 'SYN' }) * console.log(isValidWithPrefix) * // Output: * // true */ apiKeyVerify(apiKey, options = {}) { if (!apiKey || typeof apiKey !== "string" || apiKey.trim().length === 0) { throw new Error("API key must be a non-empty string"); } const parts = apiKey.split("_"); if (parts.length !== 2) { throw new Error("Invalid API key format"); } const [keyPrefix, keyHash] = parts; if (keyPrefix.length !== 3 || !/^[A-Z]{3}$/.test(keyPrefix)) { throw new Error("Invalid API key prefix format"); } if (options.prefix && options.prefix !== keyPrefix) { throw new Error("API key prefix does not match expected prefix"); } if (!keyHash || keyHash.length < 16 || keyHash.length > 128) { throw new Error("Invalid API key hash length"); } if (!/^[a-fA-F0-9]+$/.test(keyHash)) { throw new Error("Invalid API key hash format"); } try { const hasValidChars = !/^[0]{16,}$|^[f]{16,}$|^[1]{16,}$/.test(keyHash); if (!hasValidChars) { return false; } const keyEntropy = new Set(keyHash.split("")).size; if (keyEntropy < 4) { return false; } return true; } catch (error) { return false; } } /** * @method otpEnrol * Enrolls a user for OTP by generating a secret and returning OTP configuration. * @param options - Configuration options for OTP enrollment. * @param options.identifier - Unique identifier for the user (email, username, etc.). * @param options.issuer - The issuer name (app/service name). Defaults to 'Synotech Ai'. * @param options.label - Label for the OTP entry. Defaults to identifier. * @param options.algorithm - HMAC algorithm to use. Defaults to 'SHA1'. * @param options.digits - Number of digits in OTP code. Defaults to 5. * @param options.period - Time period for TOTP in seconds. Defaults to 120. * @returns Object containing secret, QR code URI, and manual entry key. * * @example * const enrollment = cryptography.otpEnrol({ * identifier: 'user@example.com', * issuer: 'MyApp', * algorithm: 'SHA256', * digits: 5, * period: 120 * }) * console.log(enrollment) * // Output: * // { * // secret: 'JBSWY3DPEHPK3PXP', * // uri: 'otpauth://totp/MyApp:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=MyApp', * // qr: 'otpauth://totp/MyApp:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=MyApp' * // } */ otpEnrol(options) { const { identifier, issuer = "Synotech Ai", label = identifier, algorithm = "SHA1", digits = 5, period = 120 } = options; if (!identifier) { throw new Error("Identifier is required for OTP enrollment"); } const secret = new OTPAuth__namespace.Secret({ size: 32 }); const totp = new OTPAuth__namespace.TOTP({ issuer, label, algorithm, digits, period, secret }); const uri = totp.toString(); return { secret: secret.base32, uri, qr: uri }; } /** * @method otpIssue * Generates a time-based OTP code using the provided secret. * @param options - Configuration options for OTP generation. * @param options.secret - The base32 encoded secret key. * @param options.timestamp - Optional timestamp for code generation. Defaults to current time. * @returns The generated OTP code as a string. * * @example * const code = cryptography.otpIssue({ * secret: 'JBSWY3DPEHPK3PXP' * }) * console.log(code) * // Output: '12345' * * const historicalCode = cryptography.otpIssue({ * secret: 'JBSWY3DPEHPK3PXP', * timestamp: 1640995200000 * }) * console.log(historicalCode) * // Output: '78901' */ otpIssue(options) { const { secret, timestamp } = options; if (!secret) { throw new Error("Secret is required for OTP generation"); } try { const totp = new OTPAuth__namespace.TOTP({ secret: OTPAuth__namespace.Secret.fromBase32(secret), algorithm: "SHA1", digits: 5, period: 120 }); return totp.generate(timestamp ? { timestamp } : void 0); } catch (error) { throw new Error(`Failed to generate OTP: ${error instanceof Error ? error.message : "Unknown error"}`); } } /** * @method otpVerify * Verifies a time-based OTP code against the provided secret. * @param options - Configuration options for OTP verification. * @param options.secret - The base32 encoded secret key. * @param options.token - The OTP code to verify. * @param options.window - Time window for verification (number of periods). Defaults to 1. * @param options.timestamp - Optional timestamp for verification. Defaults to current time. * @returns Boolean indicating whether the token is valid. * * @example * const isValid = cryptography.otpVerify({ * secret: 'JBSWY3DPEHPK3PXP', * token: '12345' * }) * console.log(isValid) * // Output: true or false * * const isValidWithWindow = cryptography.otpVerify({ * secret: 'JBSWY3DPEHPK3PXP', * token: '12345', * window: 2 * }) * console.log(isValidWithWindow) * // Output: true or false */ otpVerify(options) { const { secret, token, window = 1, timestamp } = options; if (!secret) { throw new Error("Secret is required for OTP verification"); } if (!token) { throw new Error("Token is required for OTP verification"); } try { const totp = new OTPAuth__namespace.TOTP({ secret: OTPAuth__namespace.Secret.fromBase32(secret), algorithm: "SHA1", digits: 5, period: 120 }); const delta = totp.validate({ token, timestamp, window }); return delta !== null; } catch (error) { throw new Error(`Failed to verify OTP: ${error instanceof Error ? error.message : "Unknown error"}`); } } /** * @method slug * Generates a unique slug using adjectives, colors, animals, names, languages, starWars, and countries. * @param options - Configuration options for slug generation. * @param options.separator - The separator to use between words. Defaults to '-'. * @param options.length - The number of words in the slug. Defaults to 3. * @returns A unique slug string. * * @example * const slug = cryptography.slug() * console.log(slug) * // Output: * // happy-blue-elephant * * const customSlug = cryptography.slug({ separator: '_', length: 5 }) * console.log(customSlug) * // Output: * // brave_red_tiger_john_french */ slug(options = {}) { const { separator = "-", length = 3 } = options; const dictionaries = [uniqueNamesGenerator.adjectives, uniqueNamesGenerator.colors, uniqueNamesGenerator.animals, uniqueNamesGenerator.names, uniqueNamesGenerator.languages, uniqueNamesGenerator.starWars, uniqueNamesGenerator.countries]; const effectiveLength = Math.min(length, dictionaries.length); const config = { dictionaries, separator, length: effectiveLength }; return uniqueNamesGenerator.uniqueNamesGenerator(config); } }; var index_default = Cryptography; exports.Cryptography = Cryptography; exports.default = index_default; //# sourceMappingURL=index.cjs.map //# sourceMappingURL=index.cjs.map