@synotech/crypto
Version:
Comprehensive cryptography library with Web3 integration, supporting symmetric/asymmetric encryption, digital signatures, JWT tokens, blockchain address generation, and secure key management
974 lines (972 loc) • 32 kB
JavaScript
import { randomBytes, createHmac, createCipheriv, createDecipheriv, pbkdf2Sync, generateKeyPairSync, createPublicKey, createSign, createVerify } from 'crypto';
import * as jwt from 'jsonwebtoken';
import * as OTPAuth from 'otpauth';
import { uniqueNamesGenerator, adjectives, colors, animals, names, languages, starWars, countries } from 'unique-names-generator';
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var SecureEntropyPool = class {
static {
__name(this, "SecureEntropyPool");
}
constructor() {
if (!randomBytes) {
throw new Error("Node.js crypto module not available");
}
}
getSecureRandomBytes(length) {
return 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 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 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 = 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 = randomBytes(this.ALG.AUTH_TAG_BYTE_LEN);
const keyBuffer = Buffer.from(this.KEYS.encryptKey, "hex");
const cipher = 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 = 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 = 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.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.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 } = 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 = 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 = createSign(`RSA-SHA256`);
sign2.update(data);
const signature = sign2.sign(privateKey, `base64`);
const verify2 = 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 = createHmac("sha256", this.KEYS.encryptKeySingle).update(basePayload).digest("hex");
while (signature.length < length) {
const additionalSignature = 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.Secret({ size: 32 });
const totp = new OTPAuth.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.TOTP({
secret: OTPAuth.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.TOTP({
secret: OTPAuth.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 = [adjectives, colors, animals, names, languages, starWars, countries];
const effectiveLength = Math.min(length, dictionaries.length);
const config = {
dictionaries,
separator,
length: effectiveLength
};
return uniqueNamesGenerator(config);
}
};
var index_default = Cryptography;
export { Cryptography, index_default as default };
//# sourceMappingURL=index.mjs.map
//# sourceMappingURL=index.mjs.map