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

2fa-wasm

v1.1.1

Published

Biblioteca 2FA zero-allocation (HOTP/TOTP/Base32, RFC 4226/6238/4648) en AssemblyScript/WebAssembly con wrapper TypeScript. Compatible con otpauth:// (Google Authenticator), validación constant-time, ~478K TOTP/s.

Downloads

442

Readme

🔐 2FA WASM

Biblioteca de autenticación de dos factores (2FA) ultra-rápida y de alto rendimiento compilada en WebAssembly (WASM) utilizando AssemblyScript y empaquetada con un wrapper TypeScript Zero-Allocation.

Soporta HOTP (RFC 4226), TOTP (RFC 6238) y Base32 (RFC 4648) sobre HMAC-SHA1 / SHA256 / SHA512 —con validación de tokens en tiempo constante (a prueba de timing attacks) y compatibilidad total con otpauth:// (Google Authenticator, Authy, etc.)— con rendimiento extremo (~478 K TOTP/s rotando los 3 algoritmos) y 0 % de presión sobre el Garbage Collector (GC).


🚀 Características Principales

  • Máximo Rendimiento: Núcleo HMAC desplegado en WebAssembly con ~478 000 TOTP/s (rotando SHA-1/256/512) y memoria completamente plana bajo carga.
  • 🧹 Zero-Alloc (Cero Alocaciones GC): Alocador arena estático (memory.data + save/restore) y contextos hash como struct sobre puntero (changetype, sin new en el heap) — WASM Δ 0 B verificado bajo 500 000 operaciones.
  • 🌐 Multiplataforma: Funciona sin modificaciones en Node.js, Bun, Deno y Navegadores Web (Vite, Webpack, etc.).
  • 🛡️ Seguridad Crypto:
    • HMAC-SHA1 / SHA256 / SHA512 (RFC 2104) como primitiva de HOTP/TOTP.
    • Validación constant-time de tokens: comparación por XOR acumulativo sin early-exit, inmune a ataques de timing.
    • Correctitud validada contra vectores oficiales (RFC 4226 Ap. D, RFC 6238 Ap. B, RFC 4648) y node:crypto (36/36 tests).
  • 🔑 HOTP / TOTP: generadores y validadores con ventana de tolerancia, 4–8 dígitos, y los 3 algoritmos (SHA-1 por compatibilidad, SHA-256/512 para mayor seguridad).
  • 📱 Compatibilidad otpauth://: toString() genera URIs escaneables y URI.parse() las reconstruye — integración directa con Google Authenticator, Authy, 1Password, etc.
  • 🔐 Secrets seguros: generación aleatoria con crypto.getRandomValues y conversiones Base32/hex.
  • 📦 Binario Compacto: ~14 KB de WebAssembly optimizado.

📦 Instalación

npm install 2fa-wasm

O con Bun / Yarn / pnpm:

bun add 2fa-wasm

💻 Guía de Uso

1. Inicializar la biblioteca

import { TwoFaWasm } from "2fa-wasm"

// Carga automática desde la URL por defecto (navegador / bundler)
await TwoFaWasm.load()

// O cargando desde un buffer binario explícito (útil para Node.js / Bun):
// import { readFileSync } from "node:fs"
// const wasmBuffer = readFileSync("node_modules/2fa-wasm/dist/2fa.wasm")
// await TwoFaWasm.fromBuffer(wasmBuffer)

La carga se realiza una sola vez. Después puedes usar TOTP / HOTP / Secret / Base32 / URI directamente (usan el singleton global).

2. TOTP (Time-based OTP — RFC 6238)

El caso de uso más común (Google Authenticator y compatibles).

import { TwoFaWasm, TOTP, Secret } from "2fa-wasm"

await TwoFaWasm.load()

// 🔐 Genera un secret nuevo (20 bytes, típico para SHA-1)
const secret = new Secret()
console.log(secret.base32) // → "JBSWY3DPEHPK3PXP…" (para mostrar al usuario / QR)

// 🔑 Genera el código TOTP actual (6 dígitos)
const code = TOTP.generate({ secret })
console.log(code) // → "123456"

// ✅ Valida un código ingresado por el usuario (ventana ±1 por defecto)
const delta = TOTP.validate({ token: code, secret })
console.log(delta) // → 0 (período exacto) | ±1 (ventana) | null (inválido)

// ⏱️ Segundos restantes del período actual
const totp = new TOTP({ secret, period: 30 })
console.log(totp.remaining()) // → 18

🛡️ Para mayor seguridad usa SHA-256/512 y un secret de 32/64 bytes: new TOTP({ secret: new Secret({ size: 32 }), algorithm: "SHA256" }).

3. HOTP (Counter-based OTP — RFC 4226)

import { TwoFaWasm, HOTP, Secret } from "2fa-wasm"

await TwoFaWasm.load()

const secret = Secret.fromBase32("JBSWY3DPEHPK3PXP")

// 🔑 Genera el código HOTP para un counter (vector RFC 4226)
const code = HOTP.generate({ secret, counter: 0 })
console.log(code) // → "755224"

// ✅ Valida con ventana de tolerancia (±window)
const delta = HOTP.validate({ token: "287082", secret, counter: 0, window: 1 })
console.log(delta) // → 1 (el token corresponde al counter 1, dentro de la ventana)

// 🔄 API con estado: el counter se auto-incrementa en cada generate()
const hotp = new HOTP({ secret, counter: 0 })
hotp.generate() // counter 0 → luego incrementa a 1
hotp.generate() // counter 1 → luego incrementa a 2

4. Secret (gestión de la clave compartida)

import { TwoFaWasm, Secret } from "2fa-wasm"

await TwoFaWasm.load()

// 🎲 Aleatorio (20 bytes por defecto; 32/64 para SHA-256/512)
const s1 = new Secret()
const s2 = new Secret({ size: 32 })

// 📥 Desde un Base32 existente (pegado por el usuario)
const s3 = Secret.fromBase32("JBSWY3DPEHPK3PXP")

console.log(s3.base32) // → "JBSWY3DPEHPK3PXP"
console.log(s3.hex) // → "48656c6c6f21deadbeef"
console.log(s3.bytes) // → Uint8Array (bytes crudos)

5. Base32 (RFC 4648)

import { TwoFaWasm, Base32 } from "2fa-wasm"

await TwoFaWasm.load()

const encoded = Base32.encode(new TextEncoder().encode("foobar"))
console.log(encoded) // → "MZXW6YTBOI"

const decoded = Base32.decode("MZXW6YTBOI") // → Uint8Array (bytes de "foobar")

// 🧹 Decode tolerante: acepta lowercase, espacios y padding (típico de secrets pegados a mano)
Base32.decode("mzxw 6ytb oi======") // → mismos bytes

6. URI otpauth:// (compatible con Google Authenticator)

Genera y parsea URIs estándar para códigos QR y migración entre apps.

import { TwoFaWasm, TOTP, URI } from "2fa-wasm"

await TwoFaWasm.load()

const totp = new TOTP({
  issuer: "MiApp",
  label: "[email protected]",
  secret: "JBSWY3DPEHPK3PXP",
  algorithm: "SHA1",
  digits: 6,
  period: 30
})

// 📱 Genera la URI otpauth:// (para el código QR)
const uri = totp.toString()
// → "otpauth://totp/MiApp:usuario%40ejemplo.com?secret=JBSWY3DPEHPK3PXP&algorithm=SHA1&digits=6&period=30&issuer=MiApp"

// 📥 Reconstruye desde una URI (ej. escaneada o importada)
const parsed = URI.parse(uri)
console.log(parsed.issuer) // → "MiApp"
console.log(parsed.generate()) // → código TOTP actual

URI.parse() devuelve una instancia HOTP o TOTP según el tipo de URI, lista para generate()/validate().

7. API de bajo nivel (TwoFaWasm)

Acceso directo a las primitivas del WASM (uso avanzado).

import { TwoFaWasm } from "2fa-wasm"

const wasm = await TwoFaWasm.load()

// 🔏 HMAC directo (SHA1 / SHA256 / SHA512)
const mac = wasm.hmac("SHA256", clave, mensaje) // → Uint8Array

// ⏱️ Counter y segundos restantes de TOTP
const counter = wasm.totpCounter(30) // counter del período actual
const remaining = wasm.totpRemaining(30) // segundos restantes

// 🔑 Generación/validación raw (bytes, sin formatear)
const code = wasm.hotpGenerate(secretBytes, 0, 6, "SHA1") // → "755224"
const delta = wasm.totpValidate("123456", secretBytes, Date.now(), 30, 6, 1, "SHA1")

8. Utilidades

import { bytesToHex, hexToBytes, toUint8Array, getRandomBytes } from "2fa-wasm"

const hex = bytesToHex(new Uint8Array([0xde, 0xad, 0xbe, 0xef])) // "deadbeef"
const bytes = hexToBytes("deadbeef") // Uint8Array(4)
const utf8 = toUint8Array("texto") // string → Uint8Array (UTF-8)
const random = getRandomBytes(32) // 32 bytes aleatorios (crypto-safe)

📊 Benchmarks de Rendimiento

Pruebas ejecutadas en Bun (JavaScriptCore) sobre un procesador x86-64 moderno, en single-thread:

| Operación | Rendimiento | Memoria | | ------------------------------------ | ---------------------- | -------------- | | TOTP generate (3 algoritmos rotados) | ~478 K ops/s | — | | Estrés 500 000 ops | estable (~478 K ops/s) | WASM Δ 0 B | | Heap JS tras 500 000 ops | — | Δ ~0.02 MB |

Rondas de estrés (zero-alloc verificado):

| Iteraciones | Ops/seg | Heap Δ | WASM Δ | | ----------- | ------- | ------- | ------- | | 10 000 | 328 185 | 0.00 MB | 0 B | | 50 000 | 477 666 | 0.00 MB | 0 B | | 100 000 | 486 251 | 0.00 MB | 0 B | | 500 000 | 478 387 | 0.02 MB | 0 B |

El throughput se estabiliza (~478 K ops/s) en lugar de degradarse, y la memoria lineal del WASM nunca crece (Δ 0 B) — la garantía zero-alloc se cumple bajo carga real, no solo en el caso feliz.

Validación de correctitud (36/36 tests):

| Estándar | Cobertura | | ----------------------- | -------------------------------------------------- | | HOTP — RFC 4226, Ap. D | 10 contadores (SHA-1, 6 dígitos) + ventana/delta | | TOTP — RFC 6238, Ap. B | 6 timestamps × SHA-1/256/512 (8 dígitos) | | Base32 — RFC 4648 | vectores + tolerancia (padding/lowercase/espacios) | | HMAC — vs node:crypto | SHA-1/256/512 + clave larga (> bloque) |


🏗️ Arquitectura Zero-Allocation

  • Alocador arena estático: 32 KB reservados en el data segment del WASM en tiempo de compilación (memory.data), con save()/restore() por operación — ninguna reserva toca el heap del runtime.
  • Contextos como struct sobre puntero: los contextos hash (SHA-1/256/512) se superponen sobre el scratchpad vía changetype (sin new), eliminando por completo las alocaciones en el heap.
  • Validación constant-time: hotp_validate_raw compara el token generado con el recibido mediante XOR + OR acumulativo de longitud fija (sin early-exit), evitando fugas por timing.
  • Única asignación: el string del código OTP que se entrega al usuario (inevitable); todo lo demás vive en el scratchpad.
  • Resultado verificado: WASM Δ 0 B y heap estable bajo 500 000 operaciones.

🛠️ Comandos de Desarrollo

# Compilar binario WASM (AssemblyScript) + bundle TypeScript (esbuild minificado)
bun run build

# Compilar solo el binario WASM
bun run asbuild          # release → dist/2fa.wasm
bun run asbuild:debug    # debug → build/debug.wasm + .wat + sourcemap

# Verificación de tipos
bun run typecheck

# Tests (suite de correctitud + estrés zero-alloc)
bun test

📁 Estructura del Proyecto

2fa-wasm/
├── assembly/            # Núcleo en AssemblyScript (→ WebAssembly)
│   ├── index.ts         # Exportaciones WASM (hmac, base32, hotp, totp)
│   ├── memory.ts        # Alocador arena estático (Memory: alloc/save/restore)
│   ├── sha1.ts          # SHA-1 (struct sobre puntero, compress desplegado)
│   ├── sha2/
│   │   ├── sha256.ts    # SHA-256 (sliding-window)
│   │   ├── sha512.ts    # SHA-512 (sliding-window)
│   │   ├── constants.ts # Constantes K e IV
│   │   └── common.ts    # Helpers (bswap, Ch, Maj, Sigma, sigma)
│   ├── hmac.ts          # Dispatch HMAC interno (SHA1/256/512)
│   ├── hotp.ts          # HOTP (RFC 4226, validación constant-time)
│   ├── totp.ts          # TOTP (RFC 6238)
│   └── base32.ts        # Base32 (RFC 4648, decode tolerante)
├── src/                 # Wrapper en TypeScript
│   ├── index.ts         # Punto de entrada (re-exports)
│   ├── twofa-wasm.ts    # Cargador (load/fromUrl/fromBuffer) + API bajo nivel
│   ├── allocator.ts     # WasmAllocator (caché de heap, zero-alloc)
│   ├── base32.ts        # Base32
│   ├── secret.ts        # Secret (bytes/base32/hex)
│   ├── hotp.ts          # HOTP + interfaces
│   ├── totp.ts          # TOTP + interfaces
│   ├── uri.ts           # Parser otpauth://
│   └── types.ts         # Tipos (HashAlgorithm, configs) + utilidades
├── test/
│   ├── index.test.ts    # Suite de correctitud (RFC 4226/6238/4648, node:crypto)
│   └── stress.test.ts   # Estrés zero-alloc (WASM Δ 0 B)
├── dist/                # Build (generado)
├── asconfig.json        # Configuración de AssemblyScript
├── package.json
├── tsconfig.json
├── LICENSE              # Apache-2.0
└── README.md

📜 Licencia

Apache License 2.0 © Edison Manrique

Exención de responsabilidad: este software se proporciona "tal cual" (AS IS), sin garantía de ningún tipo. Aunque HOTP, TOTP, HMAC y Base32 son estándares (RFC 4226 / RFC 6238 / RFC 2104 / RFC 4648), esta implementación no ha sido auditada formalmente y no debe emplearse en aplicaciones de seguridad crítica sin verificación independiente.