Subject: [core] Examples of lock-step protocols

From: Christian Amsüss <[email-masked]>
Date: Tue, 8 Jun 2021 16:52:02 +0200

Hello CoRE,
hello implementers of lock-step authentication protocols on top of it,

based on discussion around how to implement EAP on CoAP, and in
parallel on EDHOC discussions (and triggered by Carsten's clarifying
comment in today's ACE meeting), I've expanded the CoAP FAQ[1] by an
item on how lock-step protocols are best done.

(Full text below the mail).

EAP is given as a best-practice example; to EDHOC it doesn't apply in
full, and EDHOC does to an extent need to go its own way, especially as
the connection identifiers are part of the running protocol (given
they're used later on, eg. for OSCORE Sender-IDs), and need more
expressitivity than just the UTF-8 strings usable inside paths.

I don't think that there is any current LWIG document where it'd make
sense to add it, but if so, please let me know and I'd try to fit it in.

If you disagree or would like to add to it, please edit the wiki or just
continue this thread.

BR
Christian

[1]: https://github.com/core-wg/wiki/wiki/CoAP-FAQ -- prominent caveat:
  Answers are provided by various editors, can be conflicting, and do
  not necessarily reflect any working group consensus.

Full text of the new item:

Q: I'd like to run my lock-step protocol over CoAP (where one device sends a
message, then the other sends a message, until some sequence is completed), and
need a reliable way to not lose messages or get duplicate ones. How do I do
that?

A: In a typical and idiomatic solution, there is an entry point URI to which
the client POSTs the first message.

The server then sends the response in a 2.01 Created response as a payload, and
indicates in Location-Path options where the next POST request should go. The
client then POSTs the next message there, receives a new Location-Path.

A typical (easy-to-debug) pattern of URIs for a given "service" would be
/service/ as an entry point, with a random identifier (eg. 0f4ef9bc) used for
the individual exchange, and a message counter. Thus, the first POST would go
to /service/ containing the first message, the response would contain the
second message and a Uri-Location of /service/0f4ef9bc/3. The third message
would be sent to /service/0f4ef9bc/3, and the response would contain the fourth
message and /service/0f4ef9bc/5 et cetera. This is never a rule, though -- in
any such protocol, any server can pick completely arbitrary paths, and while
the protocol may place constraints on the maximum length of the path, the
client may not place assumptions on the path or read anything into it.

The server can correlate and sort the messages by structuring the addresses
used for them, and the client can correlate and sort the messages by the
request-response matching (through the token) as it knows to which of its own
messages it is the successor.

The document draft-ietf-ace-wg-coap-eap provides an example of such an
exchange.

As many constrained servers do not implement message deduplication, such a
scheme (relying on POST which is not on by default idempotent) can be
difficult. These implementations can be accommodated for by specifying that the
client may only send a request once to each received URI, and that duplicate
messages are responded to with the latest sent message. Thus, the server can
forego message deduplication on the CoAP layer for these resources, and the
implementation would just need to check whether the message number encoded in
the request URI is the expected next (in which case it's processed normally),
or whether it's the one befoer (in which case the request payload is discarded,
and the exchange's last state is used to send the last response again). (Even
earlier messages can be discarded, and answered e.g. with a 4.04 Not Found).

-- 
To use raw power is to make yourself infinitely vulnerable to greater powers.
  -- Bene Gesserit axiom