Files
skills/docs/security-stack/security/otp/src/hotp/encode-counter.md
2026-05-29 08:20:56 +01:00

2.2 KiB
Raw Blame History

hotp/encode-counter

Purpose

Encode an HOTP counter as the 8-byte big-endian block required by RFC 4226 §5.1. bigint is the canonical input type because Number loses precision past 2^53 and the RFC permits counters up to 2^64 − 1. Shared by HOTP itself, by OCRA's optional C directive, and by OCRA's T timestamp block (which is also written as an 8-byte big-endian integer per RFC 6287 §5.1).

Features

  • BigInt input — no precision loss across the full 0..2^64-1 range.
  • Range guard: negative counters and counters past 2^64-1 raise OtpError(OTP.INVALID_OPTIONS).
  • Pure: returns a freshly-allocated Uint8Array(8) per call.

Usage: Basic Example

import { encodeCounter } from "@suaveplan/otp";

const block = encodeCounter(1n);
// block === Uint8Array([0, 0, 0, 0, 0, 0, 0, 1])

Usage: Advanced Example

import { encodeCounter } from "@suaveplan/otp";

// Counter past Number.MAX_SAFE_INTEGER round-trips losslessly.
const block = encodeCounter((1n << 53n) + 1n);
// `block` is exactly 8 bytes; the high bytes carry the counter's high bits.

API Reference

  • encodeCounter(counter: bigint): Uint8Array — returns an 8-byte big-endian block. Negative input raises OtpError(OTP.INVALID_OPTIONS); values >= 2^64 raise the same code.

Implementation Notes

The function loops from byte index 7 back to 0 shifting counter right by 8 bits each step. The mask 0xffn keeps the operation inside the bigint domain so no implicit Number conversion can lose precision. The function is the single source of truth for big-endian counter framing — both HOTP and OCRA route through it so any future endianness change propagates atomically.

The upper-bound check counter > MAX_U64 catches the niche case where a caller passes a programmatically-generated counter past the RFC 4226 limit. Without the check the encoder would silently truncate the high bits and produce two distinct counters that hash to the same byte block, which would silently break the per-counter uniqueness invariant HOTP relies on. The lower-bound check on negative counters covers the symmetric case for callers who derive counters via BigInt() arithmetic that may go negative under bug conditions.