Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

@synotech/crypto

zealmurapa120MIT2.0.3TypeScript support: included

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

cryptography, encryption, web3, blockchain, digital-signatures, jwt, aes, rsa, elliptic-curve, sha256, hmac, pbkdf2, base64, secp256k1, secp521r1, address-generation, key-derivation, secure-random, typescript, nodejs

readme

Cryptography

A comprehensive TypeScript cryptography library providing secure encryption, digital signatures, JWT token management, and Web3-compatible cryptographic operations.

Features

Installation

yarn install

Environment Setup

Quick Start

Generate secure cryptographic keys:

# Generate new .env file with secure keys
node generate.js generate

# Or show keys without saving
node generate.js show

Manual Setup

Create a .env file with the following variables:

CRYPTO_AES_ENCRYPTION_KEY=<64-character-hex-string>
CRYPTO_HMAC_SIGNING_KEY=<64-character-hex-string>

Key Requirements

  • Both keys must be exactly 64 hexadecimal characters (32 bytes)
  • Keys must contain only valid hex characters (0-9, a-f, A-F)
  • Use cryptographically secure random generation

Internal Entropy Generation

The library now features built-in entropy generation that eliminates the need for external key pool files:

  • Self-contained: No external pool.json file required
  • Cryptographically secure: Uses Node.js crypto.randomBytes() for all entropy generation
  • Dynamic generation: Fresh entropy generated on-demand for each operation
  • Backward compatible: Existing code works unchanged

Usage

Basic Usage (Environment Variables)

import { Cryptography } from './index'

const crypto = new Cryptography()

const encrypted = crypto.encrypt('Hello, World!')
const decrypted = crypto.decrypt(encrypted)

const signature = crypto.signature()
const isValid = crypto.signatureVerify(signature)

const token = crypto.jwtIssue({ userId: 123 }, '1h')
const payload = crypto.jwtVerify(token)

const apiKey = crypto.apiKey()
const isValidKey = crypto.apiKeyVerify(apiKey)

// OTP (One-Time Password) functionality
const otpEnrollment = crypto.otpEnrol({ identifier: 'user@example.com', issuer: 'MyApp' })
const otpCode = crypto.otpIssue({ secret: otpEnrollment.secret })
const isValidOTP = crypto.otpVerify({ secret: otpEnrollment.secret, token: otpCode })

const slug = crypto.slug()
const customSlug = crypto.slug({ separator: '_', length: 5 })

Custom Configuration

const crypto = new Cryptography({
    encryptKey: 'your64hexencryptionkey...',
    encryptKeySingle: 'your64hexsigningkey...',
})

Mixed Configuration

const crypto = new Cryptography({
    encryptKey: 'explicit64hexkey...',
    // encryptKeySingle will use CRYPTO_HMAC_SIGNING_KEY from environment
})

🎮 Interactive Demo

Experience all cryptography features through our beautiful web interface:

# Start the demo server
npm run demo

# Open in browser: http://localhost:8080

Demo Features

  • 🎲 Key Generation: Random strings, predefined strengths, salts, crypto addresses
  • 🔒 Encryption: AES-256-GCM, one-way encryption, password hashing
  • ✍️ Digital Signatures: Generation, verification, key pairs
  • 🎫 JWT Tokens: Issue and verify tokens with custom payloads
  • 🔑 API Keys: Secure API key generation and verification with custom prefixes
  • 🏷️ Slug Generation: Human-readable unique identifiers with multiple dictionaries
  • 🔧 Utilities: Base64 encoding, environment key generation

The demo interface provides an interactive way to test all cryptographic operations with a modern, responsive design inspired by professional crypto tools.

API Reference

Constructor

The Cryptography class now features internal entropy generation for enhanced security and self-containment.

import { Cryptography } from './index'

// Basic initialization with environment variables
const crypto = new Cryptography()

// Custom configuration with explicit keys
const crypto = new Cryptography({
    encryptKey: 'your64hexencryptionkey...',
    encryptKeySingle: 'your64hexsigningkey...',
})

// Mixed configuration (some explicit, some from environment)
const crypto = new Cryptography({
    encryptKey: 'explicit64hexkey...',
    // encryptKeySingle will use CRYPTO_HMAC_SIGNING_KEY from environment
})

Constructor Options:

  • encryptKey?: string - AES-256 encryption key (64 hex chars) or uses CRYPTO_AES_ENCRYPTION_KEY env var
  • encryptKeySingle?: string - HMAC signing key (64 hex chars) or uses CRYPTO_HMAC_SIGNING_KEY env var

Internal Entropy Features:

  • Automatic entropy generation: No external files required
  • Cryptographically secure: Uses Node.js crypto.randomBytes()
  • Dynamic key generation: Fresh entropy for each operation
  • Backward compatible: Existing code works unchanged

Random String Generation

random(options: RandomOptions): string

Generate cryptographically secure random strings with customizable character sets.

Options:

  • length?: number - String length (default: 32)
  • useLowerCase?: boolean - Include lowercase letters (default: true)
  • useUpperCase?: boolean - Include uppercase letters (default: true)
  • useNumbers?: boolean - Include numbers (default: true)
  • useSpecial?: boolean - Include special characters (default: false)
  • useHex?: boolean - Include hex characters (default: false)
const random = crypto.random({})

console.log(random) // 'a1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6'

const password = crypto.random({
    length: 20,
    useLowerCase: true,
    useUpperCase: true,
    useNumbers: true,
    useSpecial: true,
})

console.log(password) // '*ajz:74,*ak0mN$9Hx2!'

const lowercase = crypto.random({
    length: 15,
    useLowerCase: true,
    useUpperCase: false,
    useNumbers: false,
    useSpecial: false,
})

console.log(lowercase) // 'abcdefghijklmno'

const hexString = crypto.random({
    length: 12,
    useLowerCase: false,
    useUpperCase: false,
    useNumbers: false,
    useSpecial: false,
    useHex: true,
})

console.log(hexString) // 'A1B2C3D4E5F6'

const edgeCase = crypto.random({ length: 1 })
console.log(edgeCase.length) // 1

const largeRandom = crypto.random({ length: 1000 })
console.log(largeRandom.length) // 1000

const uniqueResults = []
for (let i = 0; i < 100; i++) {
    uniqueResults.push(crypto.random({ length: 20 }))
}

console.log(new Set(uniqueResults).size) // 100 (all unique)

get(strength: KeyStrength): string

Generate predefined strength keys for specific use cases.

Strength Options:

  • decent_pw - 10 characters, alphanumeric
  • strong_pw - 15 characters, alphanumeric + special
  • ft_knox_pw - 30 characters, alphanumeric + special
  • ci_key - 32 characters, alphanumeric
  • 160_wpa - 20 characters, alphanumeric + special
  • 504_wpa - 63 characters, alphanumeric + special
  • 64_wep - 5 characters, hex only
  • 128_wep - 13 characters, hex only
  • 152_wep - 16 characters, hex only
  • 256_wep - 29 characters, hex only
crypto.get('decent_pw')
crypto.get('strong_pw') // 'i=SQ_qa3W[<RxoM'
crypto.get('ft_knox_pw') // 'P}U%H\OOYAYb;wc"3hgI,3Lz[gd-z]'
crypto.get('ci_key') // 'CeXHpM3nDgzdv0o3AkMCs3OuxzepLGW8'
crypto.get('160_wpa') // 'oHI#gR8z#h7BS>cZ!zH('
crypto.get('504_wpa') // '<os[g`s}u06jqt"Ea]t11,HsI[UipHD)%F";:9RhJ@kTU8GknLpMAXtoCzsJjT`'
crypto.get('64_wep') // '8911B'
crypto.get('128_wep') // '9F4E4F933BCCC'
crypto.get('152_wep') // '695E1EE96E483961'
crypto.get('256_wep') // 'AC7E866246BA6B71BF5D88A6861AB'

salt(): string

Generate cryptographic salt for password hashing.

const salt = crypto.salt()

console.log(salt) // '5eb63bbbe01eeed093cb22bb8f5acdc3'
console.log(salt.length) // 32 (16 bytes hex-encoded)

Encryption Methods

encrypt(data: string): string

Two-way AES-256-GCM encryption with authentication.

const plaintext = 'Hello, World!'
const encrypted = crypto.encrypt(plaintext)

const unicode = crypto.encrypt('🚀 Hello 世界 🌍')
const special = crypto.encrypt('!@#$%^&*()_+-=[]{}|;:,.<>?`~')

const number = crypto.encrypt(12345) // '12345'
const boolean = crypto.encrypt(true) // 'true'
const nullValue = crypto.encrypt(null) // 'null'
const undefinedValue = crypto.encrypt(undefined) // 'undefined'

const largeText = crypto.encrypt('a'.repeat(10000))

const jsonData = crypto.encrypt(
    JSON.stringify({
        message: 'Hello',
        number: 42,
        nested: { array: [1, 2, 3] },
    })
)

const multilineText = crypto.encrypt(`Line 1
Line 2
Line 3`)

const empty = crypto.encrypt('')

const differentForSameInput1 = crypto.encrypt('test')
const differentForSameInput2 = crypto.encrypt('test')

console.log(differentForSameInput1 !== differentForSameInput2) // true

decrypt(encryptedData: string): string

Decrypt AES-256-GCM encrypted data with authentication verification.

const plaintext = 'Hello, World!'
const encrypted = crypto.encrypt(plaintext)
const decrypted = crypto.decrypt(encrypted)

console.log(decrypted) // 'Hello, World!'

const original = '🚀 Unicode test 中文'
const encrypted = crypto.encrypt(original)
const decrypted = crypto.decrypt(encrypted)

console.log(decrypted === original) // true

try {
    crypto.decrypt('invalid:format:here')
} catch (error) {
    console.log('Decryption failed - invalid format')
}

encryptSingle(data: string): string

One-way HMAC-based encryption (deterministic output).

const data = 'Hello, World!'
const hash1 = crypto.encryptSingle(data)
const hash2 = crypto.encryptSingle(data)

console.log(hash1 === hash2) // true

const hash3 = crypto.encryptSingle('Different data')

console.log(hash1 !== hash3) // true
console.log(hash1) // 'YWxnQmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejEyMzQ1Ng=='

const numericHash = crypto.encryptSingle(12345)
const booleanHash = crypto.encryptSingle(true)
const nullHash = crypto.encryptSingle(null)

Digital Signatures

signature(): string

Generate encrypted digital signature using internal keys.

const signature = crypto.signature()

console.log(signature.split(':').length) // 3 components

signatureVerify(signature: string): boolean

Verify digital signature authenticity.

const signature = crypto.signature()
const isValid = crypto.signatureVerify(signature)

console.log(isValid) // true

try {
    const invalidSig = crypto.encrypt('unknown1 unknown2')
    crypto.signatureVerify(invalidSig)
} catch (error) {
    console.log('Unknown identifier')
}

Cryptographic Address Generation

address(): string

Generate blockchain-compatible crypto addresses.

const address = crypto.address()

console.log(address.length) // 32 characters
console.log(address.startsWith('k')) // true
console.log(address.match(/^k[a-zA-Z0-9]+$/)) // Valid format

const addresses = Array.from({ length: 5 }, () => crypto.address())
console.log(new Set(addresses).size) // 5 (all unique)

Password Hashing

password(password: string, salt: string): string

Generate secure password hash using PBKDF2.

const password = 'mySecurePassword123'
const salt = crypto.salt()
const hash = crypto.password(password, salt)
console.log(hash.length) // 512 characters (256 bytes hex-encoded)

const hash1 = crypto.password('test', salt)
const hash2 = crypto.password('test', salt)
console.log(hash1 === hash2) // true

const salt1 = crypto.salt()
const salt2 = crypto.salt()
const hash3 = crypto.password('test', salt1)
const hash4 = crypto.password('test', salt2)
console.log(hash3 !== hash4) // true

const unicodeHash = crypto.password('🚀 Password 世界', salt)
const specialHash = crypto.password('!@#$%^&*()', salt)

Key Pair Generation

privateKey(): string

Generate elliptic curve private key (secp521r1).

const privateKey = crypto.privateKey()
console.log(privateKey.includes('-----BEGIN PRIVATE KEY-----')) // true
console.log(privateKey.includes('-----END PRIVATE KEY-----')) // true
console.log(privateKey.length > 200) // true

const key1 = crypto.privateKey()
const key2 = crypto.privateKey()
console.log(key1 !== key2) // true

publicKey(privateKey: string): string

Derive public key from private key.

const privateKey = crypto.privateKey()
const publicKey = crypto.publicKey(privateKey)

console.log(publicKey.includes('-----BEGIN PUBLIC KEY-----')) // true
console.log(publicKey.includes('-----END PUBLIC KEY-----')) // true

const publicKey1 = crypto.publicKey(privateKey)
const publicKey2 = crypto.publicKey(privateKey)
console.log(publicKey1 === publicKey2) // true

try {
    crypto.publicKey('invalid-key')
} catch (error) {
    console.log('Invalid private key format')
}

publicKeyVerify({ privateKey, publicKey }): boolean

Verify key pair authenticity using digital signatures.

const privateKey = crypto.privateKey()
const publicKey = crypto.publicKey(privateKey)
const isValid = crypto.publicKeyVerify({ privateKey, publicKey })
console.log(isValid) // true

const privateKey1 = crypto.privateKey()
const privateKey2 = crypto.privateKey()
const publicKey2 = crypto.publicKey(privateKey2)

try {
    crypto.publicKeyVerify({
        privateKey: privateKey1,
        publicKey: publicKey2,
    })
} catch (error) {
    console.log('Failed to authenticate the public key')
}

JWT Token Management

jwtIssue(payload: object, expiresIn: string): string

Create signed JWT tokens with custom payloads.

const payload = {
    userId: '12345',
    username: 'testUser',
    role: 'admin',
}
const token = crypto.jwtIssue(payload, '1h')
console.log(token.split('.').length) // 3 (header.payload.signature)

const token1h = crypto.jwtIssue(payload, '1h') // 1 hour
const token30m = crypto.jwtIssue(payload, '30m') // 30 minutes
const token7d = crypto.jwtIssue(payload, '7d') // 7 days

const complexPayload = {
    userId: 'user_12345',
    username: 'john.doe@example.com',
    roles: ['admin', 'user', 'moderator'],
    permissions: {
        read: true,
        write: true,
        delete: false,
    },
    metadata: {
        lastLogin: '2025-07-10T10:00:00Z',
        loginCount: 42,
    },
}
const complexToken = crypto.jwtIssue(complexPayload, '2h')

const minimalToken = crypto.jwtIssue({}, '1h')

const specialPayload = {
    specialChars: '!@#$%^&*()_+-=[]{}|;\':",./<>?',
    unicode: '🔐 Security test 안전',
}
const specialToken = crypto.jwtIssue(specialPayload, '1h')

jwtVerify(token: string): object

Verify and decode JWT tokens.

const payload = { userId: '12345', role: 'admin' }
const token = crypto.jwtIssue(payload, '1h')
const verified = crypto.jwtVerify(token)

console.log(verified.userId) // '12345'
console.log(verified.role) // 'admin'
console.log(verified.iat) // Issue time (timestamp)
console.log(verified.exp) // Expiry time (timestamp)

const complexPayload = {
    array: [1, 2, 3],
    nested: { level1: { level2: { level3: 'deep' } } },
}
const complexToken = crypto.jwtIssue(complexPayload, '1h')
const complexVerified = crypto.jwtVerify(complexToken)

console.log(complexVerified.array) // [1, 2, 3]
console.log(complexVerified.nested.level1.level2.level3) // 'deep'

try {
    crypto.jwtVerify('invalid.token.format')
} catch (error) {
    console.log('Invalid token')
}

try {
    crypto.jwtVerify('header.payload.wrongsignature')
} catch (error) {
    console.log('Invalid signature')
}

Slug Generation

slug(options?: SlugOptions): string

Generate unique, human-readable slugs using multiple dictionaries.

TypeScript Interface:

interface SlugOptions {
    separator?: string
    length?: number
}

Options:

  • separator?: string - Separator between words (default: '-')
  • length?: number - Number of words in slug (default: 3)

Available Dictionaries: Uses all available dictionaries automatically: adjectives, colors, animals, names, languages, starWars, and countries for maximum variety and uniqueness.

// Basic slug generation
const slug = crypto.slug()
console.log(slug) // 'happy-blue-elephant'

// Custom separator and length
const customSlug = crypto.slug({
    separator: '_',
    length: 5,
})
console.log(customSlug) // 'brave_red_tiger_john_french'

// Different separators
const dotSlug = crypto.slug({ separator: '.', length: 4 })
console.log(dotSlug) // 'quick.green.vader.spain'

const spaceSlug = crypto.slug({ separator: ' ', length: 2 })
console.log(spaceSlug) // 'dark purple'

// Generate multiple unique slugs
const slugs = Array.from({ length: 5 }, () => crypto.slug())
console.log(new Set(slugs).size) // 5 (all unique)

// Handle length exceeding available dictionaries
const maxSlug = crypto.slug({ length: 10 })
console.log(maxSlug.split('-').length) // 7 (limited to available dictionaries)

// Single word slug
const singleSlug = crypto.slug({ length: 1 })
console.log(singleSlug) // 'magnificent'

Base64 Utilities

base64Encode(data: string): string

Encode strings to Base64 format.

const data = 'Hello, World!'
const encoded = crypto.base64Encode(data)

console.log(encoded) // 'SGVsbG8sIFdvcmxkIQ=='

const unicode = crypto.base64Encode('🚀 Hello 世界 🌍')
console.log(unicode) // Base64 representation of unicode string

const special = crypto.base64Encode('!@#$%^&*()_+-=[]{}|;:,.<>?`~')

const empty = crypto.base64Encode('')
console.log(empty) // ''

const large = crypto.base64Encode('a'.repeat(1000))
console.log(large.length > 1000) // true

base64Decode(encodedString: string): string

Decode Base64 strings to original format.

const encoded = 'SGVsbG8sIFdvcmxkIQ=='
const decoded = crypto.base64Decode(encoded)
console.log(decoded) // 'Hello, World!'

const original = 'Hello, World! 🚀'
const encoded = crypto.base64Encode(original)
const decoded = crypto.base64Decode(encoded)
console.log(decoded === original) // true

const largeData = 'a'.repeat(1000)
const encodedLarge = crypto.base64Encode(largeData)
const decodedLarge = crypto.base64Decode(encodedLarge)
console.log(decodedLarge === largeData) // true

try {
    crypto.base64Decode('invalid base64!')
} catch (error) {
    console.log('Invalid base64 string')
}

isBase64Encoded(data: string): boolean

Validate Base64 string format.

console.log(crypto.isBase64Encoded('SGVsbG8sIFdvcmxkIQ==')) // true
console.log(crypto.isBase64Encoded('YW55')) // true (no padding)
console.log(crypto.isBase64Encoded('')) // true (empty)

console.log(crypto.isBase64Encoded('Hello, World!')) // false
console.log(crypto.isBase64Encoded('SGVsbG8@IFdvcmxkIQ==')) // false (invalid char)

const testData = 'Test validation'
const encoded = crypto.base64Encode(testData)
console.log(crypto.isBase64Encoded(encoded)) // true
console.log(crypto.isBase64Encoded(testData)) // false

const testCases = ['Hello', 'SGVsbG8=', '123456', 'MTIzNDU2']
testCases.forEach((test) => {
    console.log(`${test}: ${crypto.isBase64Encoded(test)}`)
})

API Key Management

apiKey(options?: ApiKeyOptions): string

Generate secure API keys with HMAC-based authentication.

Options:

  • prefix?: string - 3-letter uppercase prefix (default: 'SYN')
  • length?: number - Hash length in characters, 16-128 (default: 48)
// Generate default API key
const apiKey = crypto.apiKey()
console.log(apiKey) // 'SYN_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6'

// Custom prefix
const devKey = crypto.apiKey({ prefix: 'DEV' })
console.log(devKey) // 'DEV_x1y2z3a4b5c6d7e8f9g0h1i2j3k4l5m6n7o8p9q0r1s2t3u4v5w6'

// Custom length
const longKey = crypto.apiKey({ length: 64 })
console.log(longKey) // 'SYN_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2'

// Custom prefix and length
const customKey = crypto.apiKey({ prefix: 'XYZ', length: 32 })
console.log(customKey) // 'XYZ_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6'

apiKeyVerify(apiKey: string, options?: { prefix?: string }): boolean

Verify the authenticity and format of API keys.

Options:

  • prefix?: string - Expected prefix to validate against
// Basic verification
const apiKey = crypto.apiKey()
const isValid = crypto.apiKeyVerify(apiKey)
console.log(isValid) // true

// Verify with custom prefix
const synKey = crypto.apiKey({ prefix: 'SYN' })
const isValidSyn = crypto.apiKeyVerify(synKey)
console.log(isValidSyn) // true

// Verify with prefix validation
const isValidWithPrefix = crypto.apiKeyVerify(synKey, { prefix: 'SYN' })
console.log(isValidWithPrefix) // true

// Prefix mismatch throws error
try {
    crypto.apiKeyVerify(synKey, { prefix: 'API' })
} catch (error) {
    console.log('API key prefix does not match expected prefix')
}

Security Features:

  • HMAC-SHA256 based key generation for cryptographic security
  • Entropy validation to prevent weak patterns
  • Format validation for prefix and hash structure
  • Constant-time comparison for security against timing attacks
  • Support for custom prefixes and variable lengths

OTP (One-Time Password)

The library provides comprehensive Time-based One-Time Password (TOTP) functionality compatible with popular authenticator apps like Google Authenticator, Authy, and Microsoft Authenticator.

otpEnrol(options: OTPEnrolOptions): { secret: string; uri: string; qr: string }

Enroll a user for OTP by generating a secret and returning configuration for authenticator apps.

Options:

  • identifier: string - Unique identifier for the user (email, username, etc.) - required
  • issuer?: string - The issuer name (app/service name). Defaults to 'Synotech Ai'
  • label?: string - Label for the OTP entry. Defaults to identifier
  • algorithm?: 'SHA1' | 'SHA256' | 'SHA512' - HMAC algorithm. Defaults to 'SHA1'
  • digits?: number - Number of digits in OTP code. Defaults to 5
  • period?: number - Time period for TOTP in seconds. Defaults to 120
// Basic enrollment
const enrollment = crypto.otpEnrol({
    identifier: 'user@example.com',
})

console.log(enrollment)
// {
//     secret: 'JBSWY3DPEHPK3PXP',
//     uri: 'otpauth://totp/Synotech Ai:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Synotech%20Ai&digits=5&period=120',
//     qr: 'otpauth://totp/Synotech Ai:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Synotech%20Ai&digits=5&period=120'
// }

// Custom configuration
const customEnrollment = crypto.otpEnrol({
    identifier: 'john.doe@company.com',
    issuer: 'MyApp',
    label: 'John Doe - Production',
    algorithm: 'SHA256',
    digits: 6,
    period: 30,
})

// Use the QR code URI to generate QR codes for users
const qrCodeData = enrollment.uri
// Display this in a QR code for users to scan with their authenticator app

otpIssue(options: OTPIssueOptions): string

Generate a time-based OTP code using the provided secret.

Options:

  • secret: string - The base32 encoded secret key - required
  • timestamp?: number - Optional timestamp for code generation. Defaults to current time
// Generate current OTP code
const code = crypto.otpIssue({
    secret: 'JBSWY3DPEHPK3PXP',
})

console.log(code) // '12345' (5-digit code, valid for 120 seconds)

// Generate code for specific timestamp
const historicalCode = crypto.otpIssue({
    secret: 'JBSWY3DPEHPK3PXP',
    timestamp: 1640995200000,
})

console.log(historicalCode) // '78901'

// Use in authentication flow
const userSecret = enrollment.secret
const currentCode = crypto.otpIssue({ secret: userSecret })
console.log(`Current OTP: ${currentCode}`)

otpVerify(options: OTPVerifyOptions): boolean

Verify a time-based OTP code against the provided secret.

Options:

  • secret: string - The base32 encoded secret key - required
  • token: string - The OTP code to verify - required
  • window?: number - Time window for verification (number of periods). Defaults to 1
  • timestamp?: number - Optional timestamp for verification. Defaults to current time
// Basic verification
const isValid = crypto.otpVerify({
    secret: 'JBSWY3DPEHPK3PXP',
    token: '12345',
})

console.log(isValid) // true or false

// Verification with time window (allows codes from adjacent time periods)
const isValidWithWindow = crypto.otpVerify({
    secret: 'JBSWY3DPEHPK3PXP',
    token: '12345',
    window: 2, // Accepts codes from 2 periods before/after current time
})

// Complete authentication flow
const userSecret = 'JBSWY3DPEHPK3PXP'
const userInputCode = '12345'

if (crypto.otpVerify({ secret: userSecret, token: userInputCode })) {
    console.log('Authentication successful')
    // Grant access
} else {
    console.log('Invalid OTP code')
    // Deny access
}

// Verify historical code
const historicalVerification = crypto.otpVerify({
    secret: 'JBSWY3DPEHPK3PXP',
    token: '78901',
    timestamp: 1640995200000,
})

Complete OTP Workflow Example:

// 1. User enrollment
const enrollment = crypto.otpEnrol({
    identifier: 'user@example.com',
    issuer: 'MySecureApp',
})

// 2. Store the secret securely (encrypted in database)
const encryptedSecret = crypto.encrypt(enrollment.secret)
// Save encryptedSecret to user's record

// 3. Show QR code to user for setup
console.log('Scan this QR code with your authenticator app:')
console.log(enrollment.uri)

// 4. During login - user provides OTP code
const userEnteredCode = '12345' // From user input

// 5. Retrieve and decrypt user's secret
const decryptedSecret = crypto.decrypt(encryptedSecret)

// 6. Verify the code
const isAuthenticated = crypto.otpVerify({
    secret: decryptedSecret,
    token: userEnteredCode,
    window: 1, // Allow 1 period tolerance for clock drift
})

if (isAuthenticated) {
    // User is authenticated, proceed with login
    console.log('OTP verification successful')
} else {
    // Authentication failed
    console.log('Invalid OTP code')
}

Security Features:

  • RFC 6238 compliant TOTP implementation
  • Cryptographically secure secret generation
  • Configurable time windows for clock drift tolerance
  • Support for multiple hash algorithms (SHA1, SHA256, SHA512)
  • Base32 encoded secrets for compatibility with authenticator apps
  • Customizable code length and validity periods

Testing

Run the complete test suite:

npm test

Tests cover:

  • Constructor validation and environment variable handling
  • Encryption/decryption operations
  • JWT token management
  • API key generation and verification
  • OTP enrollment, generation, and verification
  • Digital signature operations
  • Key generation and validation

Security Features

  • AES-256-GCM: Authenticated encryption with integrity verification
  • Internal Entropy Generation: Built-in cryptographically secure random number generation using Node.js crypto.randomBytes()
  • Dynamic Key Generation: Fresh entropy generated on-demand for each operation
  • Key Validation: Automatic validation of key length and format
  • Environment Security: Secure key management via environment variables
  • Buffer Safety: Proper buffer allocation and cleanup
  • Self-contained Security: No external file dependencies for entropy generation

Development

Demo Interface

# Start interactive demo
npm run demo

# Generate environment keys
npm run demo:keys

# Show usage examples
npm run demo:show

Key Generation Script

# Generate new .env file
node generate.js generate

# Show new keys without saving
node generate.js show

# Show help
node generate.js help

Demo Script

# Run usage demonstration
node dev.js

Project Structure

├── index.ts              # Main Cryptography class with internal entropy generation
├── __tests__/            # Test suite
│   ├── __config.ts       # Test configuration
│   ├── __constructor.spec.ts
│   ├── __encrypt.spec.ts
│   └── __jwt.spec.ts
├── generate.js      # Key generation utility
├── dev.js              # Usage demonstration
└── .env                 # Environment variables (AES & HMAC keys only)

License

ISC License

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass: npm test
  5. Submit a pull request

Security Notice

⚠️ Important: Never commit cryptographic keys to version control. Always use environment variables or secure key management systems in production.