Files
2026-05-27 23:47:53 +01:00

2.5 KiB

ocra/mutual-auth

Purpose

RFC 6287 §7.1 mutual authentication helpers. Both sides compute OCRA over the concatenation of the client and server challenges, with the order flipped between the two directions (Qc || Qs for the server's proof, Qs || Qc for the client's). The module exposes pure functions plus stateful OcraMutualAuthClient / OcraMutualAuthServer wrappers so consumers don't reinvent the concatenation order.

Features

  • serverProof / verifyServerProof — the server's response and the client-side check.
  • clientProof / verifyClientProof — the client's response and the server-side check.
  • OcraMutualAuthClient / OcraMutualAuthServer — stateful wrappers that hold the suites + key + own challenge and expose narrow methods.
  • Optional extra parameter for suites that include C (counter), T (timestamp), Snnn (session info), or PSHA{...} (password hash).

Usage: Basic Example

import { OcraMutualAuthClient, OcraMutualAuthServer } from "@suaveplan/otp/ocra";

const server = new OcraMutualAuthServer(suites, key, "Qs");
const client = new OcraMutualAuthClient(suites, key, "Qc");

const Rs = server.respond("Qc");
const Rc = client.respond("Qs", Rs);
const ok = server.verify("Qc", Rc);

Usage: Advanced Example

import { clientProof, serverProof } from "@suaveplan/otp/ocra";

// Counter-bound mutual auth for a session that needs replay protection.
const Rs = serverProof({
  suites,
  key,
  clientChallenge: "Qc",
  serverChallenge: "Qs",
  extra: { counter: 7n },
});

API Reference

  • serverProof(input): string
  • verifyServerProof(input): boolean
  • clientProof(input): string — verifies server proof first; throws OtpError(OTP.VERIFY_FAILED) on mismatch.
  • verifyClientProof(input): boolean
  • OcraMutualAuthClient / OcraMutualAuthServer classes.

Implementation Notes

The challenge concatenation order is the most error-prone part of RFC 6287 §7.1 — Rs MUST be over Qc || Qs and Rc MUST be over Qs || Qc. Encoding the order in named functions and class methods keeps callers from having to reread the RFC every time they wire a new flow.

The class wrappers add a small amount of state (own challenge, own suite, own key) so consumers can keep the per-session context outside the call graph. The extra parameter lets the same flow work with counter-bound, timestamp-bound, password-hash-bound, or session-info-bound suites — the mutual-auth shape is the same, only the underlying OCRA inputs differ.