npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@otplib/core

v13.3.0

Published

Core interfaces, types, and crypto abstraction for otplib

Readme

@otplib/core

Core types, interfaces, and utilities for the otplib OTP library suite.

Overview

@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

Installation

npm install @otplib/core
pnpm add @otplib/core
yarn add @otplib/core

Core Concepts

Plugin Architecture

otplib uses a plugin architecture for both cryptographic operations and Base32 encoding:

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<Uint8Array>;
  randomBytes(length: number): Uint8Array;
}

// Base32 plugins implement encoding and decoding
interface Base32Plugin {
  name: string;
  encode(data: Uint8Array, options?: Base32EncodeOptions): string;
  decode(str: string): Uint8Array;
}

CryptoContext

The CryptoContext class provides a unified interface for crypto operations:

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);

Base32Context

The Base32Context class provides a unified interface for Base32 operations:

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");

Validation Utilities

Secret Validation

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`);
  } else if (error instanceof SecretTooLongError) {
    console.error(`Secret must not exceed ${RECOMMENDED_SECRET_BYTES} bytes`);
  }
}

Counter Validation

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})`);
  }
}

Time and Period Validation

import { validateTime, validatePeriod, MIN_PERIOD, MAX_PERIOD } from "@otplib/core";

validateTime(Math.floor(Date.now() / 1000));
validatePeriod(30); // Default TOTP period

Token Validation

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");
  }
}

Utility Functions

Counter Conversion

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]

Dynamic Truncation (RFC 4226)

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);

OTP Generation

import { truncateDigits } from "@otplib/core";

// Convert truncated value to OTP string
const otp = truncateDigits(123456789, 6);
// Output: "456789"

Constant-Time Comparison

import { constantTimeEqual } from "@otplib/core";

// Timing-safe comparison to prevent timing attacks
const isValid = constantTimeEqual("123456", "123456");
const isValid = constantTimeEqual(uint8Array1, uint8Array2);

Error Handling

All errors extend from OTPError:

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
  }
}

Type Definitions

Hash Algorithms

type HashAlgorithm = "sha1" | "sha256" | "sha512";

OTP Digits

type Digits = 6 | 7 | 8;

HOTP Options

interface HOTPOptions {
  secret: Uint8Array;
  counter: number | bigint;
  algorithm?: HashAlgorithm;
  digits?: Digits;
}

TOTP Options

interface TOTPOptions {
  secret: Uint8Array;
  epoch?: number; // Unix time in seconds
  algorithm?: HashAlgorithm;
  digits?: Digits;
  period?: number; // Time step in seconds (default: 30)
}

Verification Options

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
}

Related Packages

  • @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

Documentation

Full documentation available at otplib.yeojz.dev:

License

MIT