Skip to content

feat: Round out RFC 6238 support and validate time inputs - #26

Merged
thecodedrift merged 4 commits into
mainfrom
rfc-6238-completeness
Jul 14, 2026
Merged

feat: Round out RFC 6238 support and validate time inputs#26
thecodedrift merged 4 commits into
mainfrom
rfc-6238-completeness

Conversation

@thecodedrift

Copy link
Copy Markdown
Owner

This brings the time-based methods (and generateByCounter) closer to a full RFC 6238 implementation and tightens up how we handle bad input. Four small, self-contained commits, all backwards compatible. Every new parameter is optional with a default, so existing callers don't change.

What's in here

  • Input validation. We were feeding whatever came in straight into intval($timestamp / $window). A $window of 0 threw an uncaught DivisionByZeroError, and a negative $window or $timestamp quietly produced a nonsense counter. Both now throw an InvalidArgumentException up front.

  • Configurable HMAC algorithm. generateByCounter/generateByTime/generateByTimeWindow take an optional $algorithm (defaults to sha1), so you can generate HMAC-SHA-256 and HMAC-SHA-512 TOTPs. RFC 6238 allows these, and plenty of authenticators use them.

  • Truncation fix for wider digests. HOTPResult hard-coded the RFC 4226 dynamic-truncation offset to byte 19, the last byte of a SHA-1 digest. For SHA-256/512 that byte sits further along, so those digests were getting truncated at the wrong spot. We now read it from the digest's actual last byte. SHA-1 is unaffected.

  • T0 support. The time methods take an optional $startTime (RFC 6238's "T0", defaults to 0) to shift where time-step counting starts. There's a guard so a $startTime past the timestamp can't produce a negative step.

Testing

Added coverage for all of it, including the RFC 6238 Appendix B vectors for SHA-1, SHA-256, and SHA-512. The per-algorithm keys come from the Appendix A reference code. Full suite is green at 33 tests, and php-cs-fixer is clean.

generateByTime and generateByTimeWindow now reject a non-positive
$window (previously an uncaught DivisionByZeroError) and a negative
$timestamp (previously silently produced an unintended counter),
throwing InvalidArgumentException instead.
generateByCounter, generateByTime and generateByTimeWindow now accept
an optional $algorithm (default sha1), validated against
hash_hmac_algos().

HOTPResult::toDec previously hard-coded the dynamic-truncation offset
byte to index 19, the final byte of a SHA-1 digest, which truncated
wider SHA-256/512 digests at the wrong position. It now derives the
offset from the digest's actual final byte per RFC 4226. Verified
against the RFC 6238 Appendix B SHA-1, SHA-256 and SHA-512 vectors.
generateByTime and generateByTimeWindow accept an optional $startTime (RFC 6238 "T0", default 0) that is subtracted from the timestamp before the time step is computed, per RFC 6238 section 4.1. Documents the 2.2.0 release in the changelog.
A $startTime greater than $timestamp yields a negative effective time step and an unintended counter. generateByTime and generateByTimeWindow now reject it with InvalidArgumentException.
@thecodedrift
thecodedrift merged commit 1a80e1a into main Jul 14, 2026
12 checks passed
@thecodedrift
thecodedrift deleted the rfc-6238-completeness branch July 14, 2026 22:41
thecodedrift added a commit that referenced this pull request Jul 14, 2026
Minor bump: the RFC 6238 additions in #26 are backwards compatible (new optional parameters only), so no major/breaking change. Sets the release date and links #26 in the changelog.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant