Skip to content

feat: v2.0.0 — RFC 7231 timestamps, OpenSSL 3.x, 14 bug fixes, full test suite#22

Merged
denji merged 1 commit intomasterfrom
develop
Apr 2, 2026
Merged

feat: v2.0.0 — RFC 7231 timestamps, OpenSSL 3.x, 14 bug fixes, full test suite#22
denji merged 1 commit intomasterfrom
develop

Conversation

@denji
Copy link
Copy Markdown
Member

@denji denji commented Apr 1, 2026

Add RFC 7231/IMF-fixdate and ISO 8601 'Z' suffix timestamp parsing, port HMAC computation to the OpenSSL 3.0 EVP_MAC API, fix 14 correctness and safety bugs found during code review, and add a full Perl test suite with CI.

Timestamp Parsing

  • ISO 8601 Support: Added UTC 'Z' suffix support ("YYYY-MM-DDThh:mm:ssZ"). Previously, these strings fell through to the Unix branch and were silently misread.
  • RFC 7231 / IMF-fixdate: Added support for "Day, DD Mon YYYY hh:mm:ss GMT", including case-insensitive month-name lookup (per RFC 7231 §7.1.1.1).
  • Comma Parsing Fix: Resolved an internal-comma bug where the comma in "Sun, 06 Nov …" was mistaken for a field separator. It is now correctly skipped during boundary detection.
  • Validation Logic:
    • Extracted ngx_http_secure_link_gauss() to perform calendar-field range checks (Year ≥ 1970, Month 1–12, Day 1–31, etc.).
    • Centralized logic in ngx_http_secure_link_parse_ts(), which returns (time_t)-1 on any failure.

Security & Correctness

  • Y2038 Overflow Fix: Changed 365 * year (int calculation) to (time_t)year * 365. This promotes arithmetic to the wider type, preventing overflows on large dates.
  • Strict Unix Validation: Replaced sscanf "%llu" with a digit-only byte scan followed by ngx_atotm(). This prevents fragments like "2025" from being accepted as valid timestamps.
  • Type Mismatch Fix: Resolved a size_t vs unsigned int mismatch. Passing (u_int*)&hmac.len to HMAC() caused memory corruption on LP64 and Big-Endian systems. Now uses a local unsigned int hmac_len.
  • Variable Handling: Fixed $secure_link_hmac_expires incorrectly returning token bytes by ensuring ctx->expires points to the actual expiry-period substring.
  • OpenSSL Error Handling: Added checks for EVP_MD_size(). OpenSSL 3.0 returns -1 on error; previously, an unchecked cast produced a huge length passed to CRYPTO_memcmp.
  • Sign Logic: Refactored GMT-offset sign detection using if/else if/else to ensure unexpected characters cannot bypass the offset application.

OpenSSL Compatibility

  • API Migration: Wrapped HMAC computation in ngx_http_secure_link_hmac_compute().
    • Uses EVP_MAC on OpenSSL >= 3.0 to avoid deprecation warnings.
    • Falls back to HMAC() on OpenSSL 1.x.
  • Macro Wrappers: NGX_HMAC_MD_SIZE now abstracts EVP_MD_size (1.x) and EVP_MD_get_size (3.x).
  • Guard Rails: Added key.len overflow guards before narrowing size_t to int.

Code Quality

  • Portable sscanf: Removed incorrect (ngx_tm_t*) casts; used plain int* for %d compatibility across platforms.
  • Format String Fix: Changed %02d to %2d in sscanf. The 0 flag is a printf feature and is ignored by scanf.
  • Debug Logging: Fixed timestamp width in logs; it now uses the actual substring length (int)(ts_end - ts_start) instead of a hardcoded 25 bytes.
  • Static Analysis: Resolved all cppcheck --enable=all warnings by narrowing variable scopes and marking const pointers.

Documentation & Examples

  • Changelog: Replaced the verbose "BUGS FIXED" block with a concise summary; full rationale lives in CHANGELOG.md.
  • Separator Rules:
    • secure_link_hmac: Separator is always a comma (hardcoded).
    • secure_link_hmac_message: Separator is operator-defined (pipe, colon, slash, none, …).
  • Variable Docs: Detailed documentation for $secure_link_hmac, $secure_link_hmac_expires, and $secure_link_hmac_token regarding evaluation ordering.
  • Client Implementation Examples:
    • PHP: gmdate()
    • Node.js: toUTCString()
    • Python: isoformat() with 'Z' suffix.

@denji denji force-pushed the develop branch 4 times, most recently from a9dbf63 to 8bdab13 Compare April 2, 2026 06:30
…est suite

Add RFC 7231/IMF-fixdate and ISO 8601 'Z' suffix timestamp parsing, port
HMAC computation to the OpenSSL 3.0 EVP_MAC API, fix 14 correctness and
safety bugs found during code review, and add a full Perl test suite with
CI.

--- Timestamp parsing ---

* Add ISO 8601 UTC 'Z' suffix support ("YYYY-MM-DDThh:mm:ssZ").
  Previously fell through to the Unix branch and was silently misread.

* Add RFC 7231 / IMF-fixdate support ("Day, DD Mon YYYY hh:mm:ss GMT"),
  including case-insensitive month-name lookup (RFC 7231 §7.1.1.1).

* Fix RFC 7231 internal-comma bug: the embedded comma in "Sun, 06 Nov …"
  was previously mistaken for the field separator when an expiry field was
  also present, leaving only a 3-byte substring for the timestamp parser.
  Now detected and skipped before searching for the next field boundary.

* Extract ngx_http_secure_link_gauss() with calendar-field range checks
  (year ≥ 1970, month 1–12, mday 1–31, hour 0–23, min 0–59, sec 0–60).

* Extract ngx_http_secure_link_parse_ts() to centralise all four format
  branches; returns (time_t)-1 on any parse or range failure.

--- Security / correctness ---

* Fix Y2038 overflow: "365 * year" was int×int; large dates overflowed
  before the (time_t) cast. Fixed: (time_t)year * 365 promotes all Gauss
  arithmetic to the wider type.

* Fix strict Unix timestamp validation: replaced sscanf "%llu" fallback
  (which accepted any digit-leading string) with a digit-only byte scan
  followed by ngx_atotm(). Prevents ISO 8601 year-only values (e.g. "2025")
  from being silently accepted.

* Fix size_t/unsigned-int mismatch in token variable handler: passing
  (u_int*)&hmac.len to HMAC() wrote only 4 of 8 bytes on LP64; on
  big-endian the written bytes were in the wrong half. Use a local
  unsigned int hmac_len instead.

* Fix $secure_link_hmac_expires returning token bytes: ctx->expires was
  set to value.data/len after value had been trimmed to the token. Now
  points to the actual expiry-period substring.

* Fix EVP_MD_size() not checked: OpenSSL 3.0 returns -1 on error; the
  unchecked cast to u_int produced a huge length passed to CRYPTO_memcmp.

* Fix GMT-offset sign: two independent `if` statements replaced with
  if/else if/else so an unexpected sign character cannot silently apply
  no offset.

--- OpenSSL compatibility ---

* Wrap HMAC computation in ngx_http_secure_link_hmac_compute():
  uses EVP_MAC on OpenSSL >= 3.0 (no deprecation warning), HMAC() on 1.x.

* NGX_HMAC_MD_SIZE macro wraps EVP_MD_size (1.x) / EVP_MD_get_size (3.x).

* Add key.len overflow guard before narrowing size_t to int for HMAC().

--- Code quality ---

* Remove incorrect (ngx_tm_*_t*) casts from sscanf calls; plain int* is
  correct for %d and portable across all platforms.

* Change %02d to %2d in all sscanf format strings (the '0' flag is a
  printf-only concept; silently ignored by scanf).

* Fix debug log timestamp width: was hardcoded to 25 bytes; now uses the
  actual substring length (int)(ts_end - ts_start).

* Move gmtoff, var, i to inner scopes (cppcheck --enable=all, zero warnings).

* Declare parse_ts ts_last parameter const (cppcheck constParameterPointer).

--- Documentation ---

* Replace verbose BUGS FIXED header block with a concise changelog;
  rationale lives in CHANGELOG.md and the commit history.

* Document that the field separator in secure_link_hmac is always a comma
  (hardcoded) and the separator in secure_link_hmac_message is freely
  chosen by the operator (pipe, colon, slash, none, …).

* Document all three embedded variables ($secure_link_hmac,
  $secure_link_hmac_expires, $secure_link_hmac_token) with their exact
  possible values and evaluation ordering requirements.

* Add RFC 7231 client examples in PHP (gmdate), Node.js (toUTCString),
  and Python (isoformat + Z suffix).

* Expand Security Notes with separator guidance and URL-encoding rules.

--- Testing and CI ---

* Add t/hmac_secure_link.t: 66 Test::Nginx::Socket tests across 10
  categories (basic HMAC, Unix/ISO 8601/RFC 7231 timestamps, algorithms,
  variable values, separators, config edge cases, access-control patterns).

* Add t/lib/HmacSecureLink.pm: shared token generators, timestamp
  formatters, TS_FIXED constant (eliminates timing-race in token tests).

* Add .github/workflows/ci.yml.

* Add Makefile with build, test, test-syntax, lint, cpan-deps targets.

* Add CHANGELOG.md.
@denji denji merged commit 8cc23dd into master Apr 2, 2026
11 checks passed
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