@dotrino/opaque

中文文档
0.1.1发布于 2026/11/05MITES Module零依赖构建来源已验证近 30 天下载 4270

OPAQUE (RFC 9807) for the Dotrino ecosystem: check a password without the server ever seeing it. opaque-ke compiled to WASM in CI.

原始使用文档
文档目录6 节
  1. Qué hay dentro, y qué no
  2. Uso
  3. Errores
  4. Lo que protege y lo que no
  5. Desarrollo
  6. Documentación de uso

@dotrino/opaque

Comprobar una contraseña sin que el servidor la vea nunca, y sin entregarle a nadie algo con qué adivinarla desde fuera. Es OPAQUE (RFC 9807) para el ecosistema Dotrino. MIT.

Lo usan las tres versiones del vault —el binario, la pestaña y la extensión— y el gestor de contraseñas, para el aparato que se abre con usuario y contraseña (dotrino-passmanager/docs/temporary-access.md).

Qué hay dentro, y qué no

  • No hay criptografía escrita aquí. El protocolo es opaque-ke 4.0.1 (Meta, auditado por NCC Group en 2021), compilado a WASM desde su código fuente en nuestro CI. Este paquete fija la suite, pone nombres a las funciones y comprueba lo que entra.
  • El WASM va dentro del JS (build/wasm-bytes.js), porque el vault se distribuye como ejecutable único y ahí no se puede ir a buscar un archivo al lado.
  • Suite fija: ristretto255 + SHA-512 + 3DH, con Argon2id a 64 MiB, 3 pasadas y 4 carriles (la segunda recomendación del RFC 9106; la primera, 2 GiB, no cabe en un navegador). Los parámetros están escritos en wasm/src/lib.rs, no tomados por defecto de la librería: cambiarlos cambia los registros guardados. suiteId() los nombra.

Uso

Todo entra y sale en base64url.

import { client, server } from '@dotrino/opaque'

// Una vez por bóveda. SECRETA.
const setup = server.createSetup()

// Registro
const r1 = client.registrationStart({ password })
const response = server.registrationResponse({ setup, request: r1.request, credentialId: 'ana' })
const r2 = client.registrationFinish({ state: r1.state, response, password, identifiers })
const record = server.registrationFinish({ upload: r2.upload })        // lo que se guarda
// r2.exportKey solo sale de la contraseña: con ella se cifran las llaves del aparato.

// Inicio de sesión
const l1 = client.loginStart({ password })
const s1 = server.loginStart({ setup, record /* o null */, request: l1.request, credentialId: 'ana', identifiers })
const l2 = client.loginFinish({ state: l1.state, response: s1.response, password, identifiers })
const s2 = server.loginFinish({ state: s1.state, finalization: l2.finalization, identifiers })
// l2.sessionKey === s2.sessionKey, y l2.exportKey === r2.exportKey
  • record: null para un usuario que no existe: la respuesta es igual por fuera y falla igual. Omitirlo es un error (bad-input), no un «no existe» por defecto.
  • identifiers ({ client, server }) se atan al intercambio; si las dos puntas no pasan los mismos, falla como una contraseña equivocada.

Errores

OpaqueError con code:

codeCuándo
login-failedcontraseña equivocada, usuario inexistente, identificadores que no casan o un mensaje alterado — el mismo código a propósito
bad-inputfalta un dato o no se puede leer
protocolotro fallo del protocolo
internalun fallo que no viene del protocolo

Lo que protege y lo que no

  • Quien mira la red o se hace pasar por el servidor no consigue nada con qué adivinar.
  • Quien tiene la preparación del servidor y el registro (una copia del disco de la bóveda) sí puede probar contraseñas sin límite. Cada intento cuesta el Argon2 de arriba: medido, unos 100 ms por intento en Node con este WASM. Un atacante con hardware propio va más rápido. Lo que aguanta es la contraseña, así que tiene que ser larga.

Desarrollo

Necesita Rust (lo fija wasm/rust-toolchain.toml) y wasm-bindgen-cli en la misma versión que el crate:

cargo install wasm-bindgen-cli --version 0.2.100 --locked
npm run build          # WASM → build/
npm test               # la API, los errores y el registro v1 fijado
npm run test:vectors   # los vectores del RFC 9807 contra opaque-ke 4.0.1

La compilación es reproducible: npm run build da en cualquier máquina la misma huella que CI (build/wasm.sha256; a5a80de4… para la 0.1.0), porque scripts/build.mjs reescribe las rutas absolutas y el compilador está fijado. Así se comprueba que lo publicado sale de este fuente.

test/fixtures/v1.json es un registro guardado con la 0.1.0. Toda compilación posterior tiene que abrirlo: un registro que deja de abrir deja a alguien sin entrar.

Se publica desde CI con un tag vX.Y.Z (.github/workflows/release.yml), con el SBOM, la procedencia y la huella del WASM en la release. Nunca npm publish a mano.

Documentación de uso

Está en el wiki: https://wiki.dotrino.com/desarrollo/pilares/

版本与发布记录

@dotrino/opaque 共发布 2 个版本,首次发布于 2026/09/18;过去 12 个月发布了 2 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.1.1latest2026/11/058 天前
0.1.02026/09/181 个月前

关于 @dotrino/opaque 的常见问题

@dotrino/opaque 是什么?

OPAQUE (RFC 9807) for the Dotrino ecosystem: check a password without the server ever seeing it. opaque-ke compiled to WASM in CI.

如何安装 @dotrino/opaque?

在项目目录运行 npm install @dotrino/opaque,或使用 pnpm add / yarn add / bun add。

@dotrino/opaque 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/dotrino__opaque 是否存在。

@dotrino/opaque 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@dotrino/opaque 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@dotrino/opaque 可以商用吗?

@dotrino/opaque 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@dotrino/opaque 的下载量有多少?

根据 npm 官方下载统计,@dotrino/opaque 在 2026/10/13 – 2026/11/11 这 30 天内被下载 4,270 次,日均约 142 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@dotrino/opaque 还在维护吗?

最新版本 0.1.1 发布于 2026/11/05(8 天前);至今共发布 2 个版本,其中过去 12 个月发布了 2 个;首次发布于 2026/09/18。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。