Core interfaces, types, and crypto abstraction for otplib
npm install @otplib/coreCore types, interfaces, and utilities for the otplib OTP library suite.
@otplib/core provides the foundational abstractions for all otplib packages. It includes:
- Type Definitions: TypeScript interfaces for OTP operations
- Error Classes: Hierarchical error types for validation and crypto operations
- Validation Utilities: Input validation for secrets, counters, time, and tokens
- Crypto Abstraction: Pluggable crypto backend via CryptoContext
- Base32 Abstraction: Pluggable Base32 encoding/decoding via Base32Context
``bash`
npm install @otplib/core
pnpm add @otplib/core
yarn add @otplib/core
otplib uses a plugin architecture for both cryptographic operations and Base32 encoding:
`typescript
import type { CryptoPlugin, Base32Plugin } from "@otplib/core";
// Crypto plugins implement HMAC and random byte generation
interface CryptoPlugin {
name: string;
hmac(
algorithm: HashAlgorithm,
key: Uint8Array,
data: Uint8Array,
): Uint8Array | Promise
randomBytes(length: number): Uint8Array;
}
// Base32 plugins implement encoding and decoding
interface Base32Plugin {
name: string;
encode(data: Uint8Array, options?: Base32EncodeOptions): string;
decode(str: string): Uint8Array;
}
`
The CryptoContext class provides a unified interface for crypto operations:
`typescript
import { createCryptoContext } from "@otplib/core";
import { crypto as plugin } from "@otplib/plugin-crypto-node";
const crypto = createCryptoContext(plugin);
// Async HMAC computation
const digest = await crypto.hmac("sha1", key, data);
// Sync HMAC computation
const digest = crypto.hmacSync("sha1", key, data);
// Random bytes
const secret = crypto.randomBytes(20);
`
The Base32Context class provides a unified interface for Base32 operations:
`typescript
import { createBase32Context } from "@otplib/core";
import { base32 as plugin } from "@otplib/plugin-base32-scure";
const base32 = createBase32Context(plugin);
// Encode binary data to Base32
const encoded = base32.encode(new Uint8Array([1, 2, 3]), { padding: false });
// Decode Base32 string to binary
const decoded = base32.decode("MFRGGZDFMZTWQ");
`
`typescript
import { validateSecret, MIN_SECRET_BYTES, RECOMMENDED_SECRET_BYTES } from "@otplib/core";
try {
validateSecret(secretBytes);
} catch (error) {
if (error instanceof SecretTooShortError) {
console.error(Secret must be at least ${MIN_SECRET_BYTES} bytes);Secret must not exceed ${RECOMMENDED_SECRET_BYTES} bytes
} else if (error instanceof SecretTooLongError) {
console.error();`
}
}
`typescript
import { validateCounter, MAX_COUNTER } from "@otplib/core";
try {
validateCounter(123n);
validateCounter(0);
} catch (error) {
if (error instanceof CounterNegativeError) {
console.error("Counter cannot be negative");
} else if (error instanceof CounterOverflowError) {
console.error(Counter exceeds maximum (${MAX_COUNTER}));`
}
}
`typescript
import { validateTime, validatePeriod, MIN_PERIOD, MAX_PERIOD } from "@otplib/core";
validateTime(Math.floor(Date.now() / 1000));
validatePeriod(30); // Default TOTP period
`
`typescript
import { validateToken } from "@otplib/core";
try {
validateToken("123456", 6);
} catch (error) {
if (error instanceof TokenLengthError) {
console.error("Token has incorrect length");
} else if (error instanceof TokenFormatError) {
console.error("Token must contain only digits");
}
}
`
`typescript
import { counterToBytes } from "@otplib/core";
// Convert counter to 8-byte big-endian array
const counterBytes = counterToBytes(42n);
// Output: Uint8Array [0, 0, 0, 0, 0, 0, 0, 42]
`
`typescript
import { dynamicTruncate } from '@otplib/core';
// Extract 31-bit integer from HMAC result
const hmacResult = new Uint8Array([...]); // 20 bytes for SHA-1
const truncated = dynamicTruncate(hmacResult);
`
`typescript
import { truncateDigits } from "@otplib/core";
// Convert truncated value to OTP string
const otp = truncateDigits(123456789, 6);
// Output: "456789"
`
`typescript
import { constantTimeEqual } from "@otplib/core";
// Timing-safe comparison to prevent timing attacks
const isValid = constantTimeEqual("123456", "123456");
const isValid = constantTimeEqual(uint8Array1, uint8Array2);
`
All errors extend from OTPError:
`typescript
import {
OTPError,
SecretError,
SecretTooShortError,
SecretTooLongError,
CounterError,
CounterNegativeError,
CounterOverflowError,
TimeError,
PeriodError,
TokenError,
TokenLengthError,
TokenFormatError,
CryptoError,
HMACError,
RandomBytesError,
} from "@otplib/core";
// Check error types
try {
// ... OTP operation
} catch (error) {
if (error instanceof SecretTooShortError) {
// Handle short secret
} else if (error instanceof CryptoError) {
// Handle crypto failure
}
}
`
`typescript`
type HashAlgorithm = "sha1" | "sha256" | "sha512";
`typescript`
type Digits = 6 | 7 | 8;
`typescript`
interface HOTPOptions {
secret: Uint8Array;
counter: number | bigint;
algorithm?: HashAlgorithm;
digits?: Digits;
}
`typescript`
interface TOTPOptions {
secret: Uint8Array;
epoch?: number; // Unix time in seconds
algorithm?: HashAlgorithm;
digits?: Digits;
period?: number; // Time step in seconds (default: 30)
}
`typescript
interface HOTPVerifyOptions extends HOTPOptions {
token: string;
counterTolerance?: number | [number, number]; // Number: [0, n] look-ahead; Tuple: [past, future]
}
interface TOTPVerifyOptions extends TOTPOptions {
token: string;
epochTolerance?: number | [number, number]; // Time tolerance in seconds
}
interface VerifyResult {
valid: boolean;
delta?: number; // Counter/time steps from expected value
}
`
- @otplib/hotp - HOTP implementation@otplib/totp
- - TOTP implementation@otplib/plugin-crypto-node
- - Node.js crypto plugin@otplib/plugin-crypto-web
- - Web Crypto API plugin@otplib/plugin-base32-scure` - Base32 plugin using @scure/base
-
Full documentation available at otplib.yeojz.dev:
- Getting Started Guide
- API Reference
MIT © 2026 Gerald Yeo