Skip to content

ETag Opaque Value Is Collapsed By Integer Decoding #1997

Description

@LiD0209

ETag Opaque Value Is Collapsed By Integer Decoding

Summary

RFC 7252 defines ETag as an opaque byte string. libcoap decodes ETag bytes into uint64_t in several proxy and blockwise code paths, so byte-distinct values such as 01 and 00 01 can collapse to the same integer.

This is inconsistent with the RFC requirement that an endpoint receiving an ETag must treat it as opaque and make no assumptions about its content or structure.

  • RFC 7252 requires ETag to be treated as opaque bytes

  • libcoap still has internal paths that numerically decode ETag values

  • byte-distinct ETag values can therefore be treated as equal by integer value instead of byte identity

Standard Reference

CoAP standard: RFC 7252, Section 5.10.6, "ETag".

Relevant original English text:

An endpoint receiving an entity-tag MUST treat it as opaque and make no assumptions about its content or structure.

Relevant Source Code

libcoap-develop/src/coap_proxy.c decodes response ETag bytes into an integer before rebuilding a forwarded response:

option = coap_check_option(rcvd, COAP_OPTION_ETAG, &opt_iter);
if (option) {
  etag = coap_decode_var_bytes8(coap_opt_value(option),
                                coap_opt_length(option));
}

The proxy cache metadata also stores the ETag as an integer:

option = coap_check_option(rcvd, COAP_OPTION_ETAG, &opt_iter);
if (option) {
  proxy_cache->etag = coap_decode_var_bytes8(coap_opt_value(option),
                                             coap_opt_length(option));
} else {
  proxy_cache->etag = 0;
}

Some blockwise helper paths likewise decode ETag values numerically:

uint64_t etag_r = coap_decode_var_bytes8(coap_opt_value(etag_opt),
                                         coap_opt_length(etag_opt));
if (etag == etag_r) {
  pdu->code = COAP_RESPONSE_CODE(203);
  return 1;
}

Runtime / Probe Result

The phase 2 probe confirmed that integer decoding is present in the relevant paths.

ETag opacity probe: PASS - proxy and blockwise paths decode ETag option bytes into integers

An additional local repro is available here:

  • test-libcoap/201-250/repro_id215_etag_proxy_blockwise.c

Inconsistency Reason

Opaque comparison must preserve byte length and byte sequence. Integer decoding removes leading-zero distinctions and treats the option value as structured numeric data. That is incompatible with the RFC requirement to make no assumptions about content or structure.

Impact

Proxy response handling, cache-related metadata handling, and blockwise helper logic can confuse different ETag values that share the same decoded integer value. This can lead to incorrect ETag matching logic and protocol-semantics errors in paths that rely on those decoded values.

Related runtime boundary:

  • the separate repro test-libcoap/201-250/repro_id233_dual_etag_runtime.c
    confirmed that libcoap directly accepts a response containing multiple ETag
    options and exposes both to the application on the client receive side
  • that same repro also showed a libcoap proxy observe-cache replay path where a
    later client request carrying ETag: 01 was satisfied from stale cached
    content and only one ETag was exposed on the proxy path

That stale replay result does not by itself prove that the integer-collapse
behavior from this ID was the sole cause. It does, however, show that ETag
handling gaps are not limited to static RFC mismatch. In real intermediary
paths they can coincide with ETag selection/drop behavior and stale
representation reuse.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions