Summary

  • RFC 9668 can combine EDHOC message_3 with the first OSCORE request and reach a minimum of two round trips, but only for the forward client-Initiator/server-Responder flow and a compatible application profile.
  • Operators still need separate receipts for session lookup, message_3 verification, Security Context creation, OSCORE replay and integrity checks, application delivery, execution and the protected response.

A battery-powered controller sent one CoAP datagram toward a remote valve. Inside it were two distinct acts: the final EDHOC message needed to complete authenticated key exchange, and an OSCORE-protected instruction to change the valve. No reply came back. The operations console recorded “secure command timeout.” That label was tidy and almost useless.

The packet might have failed the combined-payload check. Its kid might have selected no live EDHOC session. The server might have rejected message_3. It might have created the Security Context and then rejected the OSCORE ciphertext as a replay. The request might have decrypted but failed application authorization. The actuator might have accepted the command but failed mechanically. The protected response might have been lost. Or the client might have rejected a response that the server had sent correctly.

RFC 9668 does not create this ambiguity carelessly. It makes a precise trade. EDHOC is designed for authenticated key exchange in constrained environments, and OSCORE protects CoAP at the application layer. In a sequential exchange, the client and server complete EDHOC before the first OSCORE transaction. That costs three round trips when no optional fourth EDHOC message is used. After receiving message_2, however, the Initiator already has what it needs to derive the OSCORE Security Context. The RFC therefore permits message_3 and the first protected request to travel together. The minimum becomes two round trips.

Fewer transmissions matter on sleepy radios and narrow links. But packet compression must not become evidence compression.

One transport envelope contains two semantic requests

The combined request is not one enlarged cryptographic blob. The client constructs EDHOC message_3, derives the new OSCORE context and protects the original CoAP request. It then concatenates the encoded message_3 with the OSCORE ciphertext. The connection identifier C_R travels separately as the client's OSCORE Sender ID in the kid field.

An empty CoAP option numbered 21 announces that structure. The option is Critical, Safe-to-Forward and part of the cache key. It may occur only once and must be empty; a recipient ignores any value. Its job is to tell the server to extract and process the EDHOC material before it tries to process the protected application request.

That distinction is operationally important. Option 21 is a parser instruction, not an authorization. A trace showing the option proves that a sender selected the combined format. It does not prove the sender had the right credential, the server found the right session, message_3 verified, OSCORE accepted the request or the application acted.

The cryptographic boundaries remain separate too. The OSCORE ciphertext is not computed over message_3. Message_3 carries EDHOC's protection; the application request carries OSCORE's protection. Sharing a datagram does not merge their security claims.

The server has nine ordered gates

RFC 9668 specifies a disciplined server sequence. First, the server checks for the OSCORE option and a well-formed combined payload. It extracts message_3, reads kid as C_R, and uses that identifier to retrieve the EDHOC session. It rejects the path if the session's application profile requires message_4. Only then does it process message_3 and derive the responder's OSCORE Security Context.

The server next extracts the OSCORE ciphertext, rebuilds the protected request without the EDHOC option, decrypts and verifies it, and finally delivers the resulting CoAP request to the application. A failure at the EDHOC gate aborts the session and forbids creating the new context. A later OSCORE failure follows OSCORE error handling. Application code is not involved until both stages have succeeded.

Collapsing these gates into one “secure request” metric discards the most useful diagnostic fact: how far the request actually travelled. A production trace should correlate one public transaction identifier with a privacy-preserving EDHOC session reference, message_3 verdict, context generation, kid, Partial IV, replay-window decision, OSCORE verdict and application request identifier. It should not log derived secrets or raw credentials.

The kid deserves special care. In the combined request it performs two jobs: it is the OSCORE Sender ID of the client and the C_R used to recover the EDHOC session. RFC 9668 adds identifier-uniqueness rules because a collision with another session or Security Context can select the wrong state. An identifier match is therefore only a lookup receipt. The generation and transcript to which it points must remain available.

A protected response closes a cryptographic loop, not every physical loop

When message_3 and OSCORE processing both succeed, the server sends an OSCORE-protected response. Verifying a response protected under the newly derived key can give the Initiator responder key confirmation: the other party computed the expected key material. OSCORE also binds a response to its request.

Those are strong properties, but their scope should stay exact. A 2.04 response can truthfully report that an application accepted a change while a downstream device has not yet moved. A queued job can be acknowledged before durable storage. A valve controller can emit a response before a limit switch confirms position. RFC 9668 does not define those application semantics.

The application receipt therefore needs its own vocabulary. Record whether the request was merely delivered, authorized, accepted for work, committed durably, observed in the physical system or later compensated. The protected response carries whichever claim the application makes; cryptography protects that claim against alteration. It does not enlarge the claim.

Two round trips are conditional, not a service-level fact

The optimized exchange only fits the default role mapping: CoAP client as EDHOC Initiator, server as Responder. The reverse EDHOC flow cannot use it. The application profile must also be consistent. If message_4 is required, the combined path is invalid. A server can advertise combined-request support through the ed-comb-req link attribute, alongside roles, methods, cipher suites and credential types. Discovery proves advertised capability, not that one transaction used it successfully.

Size can remove the advantage. Message_3 may contain a certificate chain or External Authorization Data. If the first application request also needs Block-wise transfer and the combined payload exceeds the permitted unfragmented size, the client must abandon that attempt and can return to the sequential flow: finish EDHOC first, then send OSCORE. A latency dashboard that counts only successful optimized packets will hide the population for which the optimization was least suitable.

Preserve the decision itself: payload component sizes, path MTU assumption, block number, MAX_UNFRAGMENTED_SIZE, fallback reason and resulting exchange mode. That record turns “two round trips” from a marketing average into an explainable policy outcome.

Sources