Files
skills/docs/security-stack/security/srp/src/math/k.md
2026-05-29 08:20:56 +01:00

2.4 KiB

k

Purpose

Compute the SRP-6a multiplier parameter k = H(N | PAD(g)) per RFC 5054 §2.5.3. The multiplier is the structural change that distinguishes SRP-6a from SRP-6 (the latter set k = 3, which broke under a generator-substitution attack). Mixing both N and the padded generator into a hash binds k to the chosen group so the multiplier cannot be transplanted between groups by an attacker who controls one side of the handshake.

Features

  • Pure function of (N, g, hash).
  • Returns the hash's native digest size (32 bytes for SHA-256, 20 for SHA-1).
  • Uses PAD to zero-extend g to byteLength(N) before hashing, per RFC 5054 §2.5.3.
  • Caller-side memoization safe — params is treated as immutable.

Usage: Basic Example

import { computeK } from "@suaveplan/srp";
import { params2048Sha256 } from "@suaveplan/srp/params";
import { nobleHashBridge } from "@suaveplan/srp/hash";

const params = params2048Sha256(nobleHashBridge("SHA256"));
const k = computeK(params);
console.log(k.byteLength); // 32

Usage: Advanced Example

import { computeK } from "@suaveplan/srp";
import { params2048Sha256 } from "@suaveplan/srp/params";
import { nobleHashBridge } from "@suaveplan/srp/hash";

const params = params2048Sha256(nobleHashBridge("SHA256"));
const kCached = computeK(params);
// Reuse k across many handshakes — k is a pure function of (N, g, hash).
function getCachedK(): Uint8Array {
    return kCached;
}
console.log(getCachedK().byteLength);

API Reference

computeK(params: SrpParams): Uint8Array

Returns H(N | PAD(g)) using params.hash. Result length equals params.hash.digestSize.

Implementation Notes

k is a pure function of (N, g, hash) and is therefore safe to memoize across handshakes; consumers that mount a long-lived SrpParams value can cache k outside the per-request critical path. This package does NOT cache automatically because the SrpParams.hash field MAY be a stateful subtle bridge that the consumer rotates per session; deferring the memoization decision to the caller keeps the contract simple.

The digest call goes through digestSync (from ../hash/hash-bridge.js), which throws SRP.HASH_UNSUPPORTED if the consumer accidentally wires an async HashLike into a sync handshake path. Without that gate, an awaited Promise would silently coerce to [object Promise] bytes downstream.