2.2 KiB
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-1raiseOtpError(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 raisesOtpError(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.