Summary
The TOTP/HOTP generator silently normalizes invalid secrets and accepts invalid numeric parameters. It can therefore display a plausible OTP derived from different data than the user entered.
For an authentication utility, invalid input must fail closed and explain the exact validation error; it must not silently change the key or counter.
Confirmed defects
1. Invalid Base32 characters are silently deleted
base32Decode() currently performs:
const clean = str.toUpperCase().replace(/[^A-Z2-7]/g, "")
This removes every character outside the Base32 alphabet without reporting an error. For example, these inputs are treated as the same secret:
JBSWY3DPEHPK3PXP
JBSWY3DPEHPK3PXP0
The trailing 0 is a common transcription error, but it is discarded and the tool generates a code for the shorter key. Other punctuation, Unicode characters, or misplaced padding are handled the same way.
2. A zero/invalid TOTP period can generate a misleading code
The period input is assigned with Number(event.target.value) and is not validated before use. Clearing the field produces 0.
generateTOTP() then calculates:
const counter = Math.floor(time / period)
With period 0, the counter becomes Infinity; the byte-building loop coerces it through bitwise operations and can produce an all-zero counter rather than rejecting it. The UI countdown becomes NaN, while the OTP itself can still look valid.
3. HOTP counters are not constrained to valid integer precision
The HOTP counter accepts arbitrary JavaScript numbers. Negative, fractional, non-finite, and values above Number.MAX_SAFE_INTEGER cannot be represented as a correct 64-bit moving factor by the current loop, but no validation error is shown.
4. Errors collapse to the literal ERROR
All failures are caught and rendered as ERROR, with no localized explanation and no field-level indication of what must be corrected.
Why this matters
A wrong OTP is indistinguishable from clock skew, account mismatch, or an invalid provisioning secret. Silent normalization can waste recovery time and may lead users to trust a code generated from a key they did not actually provide.
This tool handles authentication secrets, so correctness and explicit failure semantics are security-sensitive.
Recommended implementation
Extract the OTP engine and validation into a testable logic.ts module.
- Parse Base32 strictly.
- Explicitly decide which presentation characters are allowed (for example, trimming surrounding whitespace and optionally ignoring separators such as spaces/hyphens).
- Accept
= padding only in valid trailing positions, or document an unpadded-only policy.
- Reject all other characters and reject an empty decoded key.
- Validate TOTP period as a finite positive integer.
- Validate HOTP counter as a non-negative safe integer, or implement the counter with
bigint for the full RFC 4226 range.
- Return structured validation errors and disable copy/refresh actions while invalid.
Acceptance criteria
Relevant files
src/features/tools/totp-generator/page.tsx
- new/extracted
src/features/tools/totp-generator/logic.ts
- new unit/component tests for the TOTP generator
Summary
The TOTP/HOTP generator silently normalizes invalid secrets and accepts invalid numeric parameters. It can therefore display a plausible OTP derived from different data than the user entered.
For an authentication utility, invalid input must fail closed and explain the exact validation error; it must not silently change the key or counter.
Confirmed defects
1. Invalid Base32 characters are silently deleted
base32Decode()currently performs:This removes every character outside the Base32 alphabet without reporting an error. For example, these inputs are treated as the same secret:
The trailing
0is a common transcription error, but it is discarded and the tool generates a code for the shorter key. Other punctuation, Unicode characters, or misplaced padding are handled the same way.2. A zero/invalid TOTP period can generate a misleading code
The period input is assigned with
Number(event.target.value)and is not validated before use. Clearing the field produces0.generateTOTP()then calculates:With period
0, the counter becomesInfinity; the byte-building loop coerces it through bitwise operations and can produce an all-zero counter rather than rejecting it. The UI countdown becomesNaN, while the OTP itself can still look valid.3. HOTP counters are not constrained to valid integer precision
The HOTP counter accepts arbitrary JavaScript numbers. Negative, fractional, non-finite, and values above
Number.MAX_SAFE_INTEGERcannot be represented as a correct 64-bit moving factor by the current loop, but no validation error is shown.4. Errors collapse to the literal
ERRORAll failures are caught and rendered as
ERROR, with no localized explanation and no field-level indication of what must be corrected.Why this matters
A wrong OTP is indistinguishable from clock skew, account mismatch, or an invalid provisioning secret. Silent normalization can waste recovery time and may lead users to trust a code generated from a key they did not actually provide.
This tool handles authentication secrets, so correctness and explicit failure semantics are security-sensitive.
Recommended implementation
Extract the OTP engine and validation into a testable
logic.tsmodule.=padding only in valid trailing positions, or document an unpadded-only policy.bigintfor the full RFC 4226 range.Acceptance criteria
0,1, punctuation, Unicode, or invalid/misplaced padding is rejected rather than altered.Relevant files
src/features/tools/totp-generator/page.tsxsrc/features/tools/totp-generator/logic.ts