libcoap
libcoap copied to clipboard
RFC8613: Add in OSCORE support
Based on work done in https://gitlab.informatik.uni-bremen.de/obergman/libcoap
New directories with files include/oscore src/oscore
New files src/coap_oscore.c include/oscore/coap_oscore.h include/oscore/coap_crypto_internal.h include/oscore/coap_oscore_internal.h man/coap-oscore-conf.txt.in man/coap_oscore.txt.in
Supported by new option (enabled by default) ./configure --enable-oscore cmake .. -DENABLE_OSCORE=ON
Requires a TLS library configured to do the OSCORE encryption and hashing.
Added support for building with native Visual Studio.
Mainly updates to variable types to remove Windows variable size mismatch warnings.
Interesting. I had just finished my own implementation of OSCORE because I needed it. Does your implementation cover the session key establishment that is described in Appendix B.2 of the RFC?
@kkrentz Simple answer is no. kid context
is supported, but the defined protocol is not yet implemented, which is on my TODO list. I'm currently working on getting draft-ietf-core-oscore-groupcomm to work.
Was your implementation based off libcoap?
I also took Peter van der Stok's code as a basis, but did not bother retaining his implementation of group communication.
Code update pushed with more rigours buffer checking and some reworking to better support the ongoing draft-ietf-core-oscore-groupcomm work.
Code updated with more rigorous checking of buffer sizes along with some re-organization for better support of the draft-ietf-core-oscore-groupcomm work.
Added in RFC8613 Appendix C test vectors to unit tests.
Added in Appendix B.1.1 support. OSCORE specific API has been updated.
Does OSCORE specify what should happen when an RST is being received? Looking at your code, further retransmissions seem to be cancelled when an RST is being received. Since RSTs can (or even must?) be sent unauthenticated, an attacker can cancel the retransmission process, doesn't he?
@kkrentz Interesting question
Does OSCORE specify what should happen when an RST is being received?
Not that I can find, even under the security considetations. However, RFC7252 https://datatracker.ietf.org/doc/html/rfc7252#section-11.4 does state
In principle, other kinds of spoofing can be detected by CoAP only in case Confirmable message semantics is used, because of unexpected Acknowledgement or Reset messages coming from the deceived endpoint. But this imposes keeping track of the used Message IDs, which is not always possible, and moreover detection becomes available usually after the damage is already done. This kind of attack can be prevented using security modes other than NoSec.
So, when using a security model other than NoSec, this kind of RST attack cannot occur. The RFC8613 libcoap implementation can provide (D)TLS wrappers to the OSCORE packets so security modes other than NoSec are supported.
Otherwise with NoSec, the RST can only terminate a retransmission, not the initial transmission.
I ended up ignoring RSTs. Of course, if you run DTLS in addition, you can process them.
Added in configurable support for RFC8613 Appendix B.1.2 and Appendix B.2
I managed to get your OSCORE implementation to run on Contiki-NG, too. Initially, it was too RAM-consuming because in coap_oscore.c
there are some places, where pdus of maximum length are created. Besides, it was strange that server configs also must set a recipient_id
. Here are my patches:
- https://github.com/kkrentz/libcoap/commit/686d9f4af1b6e56a81d4969106477cef90fe1644
- https://github.com/kkrentz/libcoap/commit/1da61ea859ebefb7effcde320cd6faef86449991
Thanks for doing all this testing etc.
Besides, it was strange that server configs also must set a recipient_id.
I was following RFC8613 3.2 where Recipient ID shall be used to set up a security context - which provides the trust that a client is valid. I accept that the list of Recipient IDs can be dynamic as provided by some OOB mechanism or some negotiation mechanism. Why do you think it does not need to be set?
I will otherwise checkout your patches when I get a chance.
https://github.com/kkrentz/libcoap/commit/686d9f4af1b6e56a81d4969106477cef90fe1644
The Contiki change is specific to your Contiki PR #760, so cannot be a part of this PR.
The challenge I am currently having is that coap_oscore_overhead()
does not handle the expansion requirements that come about from using a Proxy-Uri option - One option can expand into 3 Options (outer) and N Options for each Uri-Path / Uri-Query. It is also complicated by the option deltas that are needed so one effectively needs to build the new inner (plain_pdu) and outer (osc_pdu) PDUs to find out what the size change will actually be.
I will look at updating coap_oscore_overhead()
to handle this so that maximum length is not always required when building the appropriate PDUs.
https://github.com/kkrentz/libcoap/commit/1da61ea859ebefb7effcde320cd6faef86449991
I will check for unintended consequences when there is no recipient_id
. There is support for dynamically adding / removing recipient IDs using coap_new_oscore_recipient()
/ coap_delete_oscore_recipient()
.
Suggestions apart from the Contiki change have now been pushed.
Thank you. The RAM consumption has gotten better. On Contiki it continues to be problematic ...
As for recipient_id
s, my implementation does not require configuring them at the server side. I extract them from the initial requests.
Code changes pushed.
Thank you. The RAM consumption has gotten better. On Contiki it continues to be problematic ...
Open to suggestions.
As for recipient_ids, my implementation does not require configuring them at the server side. I extract them from the initial requests.
It would be very simple then to DoS the server by a rogue client generating lots of kids and being smart enough to respond to any Appendix B.1.2 Echo freshness tests. How do you do any recipient context garbage collection?
Code changes pushed.
Thank you. The RAM consumption has gotten better. On Contiki it continues to be problematic ...
Open to suggestions.
In my implementation, I derive AEAD keys on demand, which saves some RAM. Besides, I do not copy the master keys, master salt, etc. to the endpoint / session structures. I just store application-provided pointers. Additionally, you may pass the exact sizes when allocating RAM for the inner and outer PDUs.
As for recipient_ids, my implementation does not require configuring them at the server side. I extract them from the initial requests.
It would be very simple then to DoS the server by a rogue client generating lots of kids and being smart enough to respond to any Appendix B.1.2 Echo freshness tests. How do you do any recipient context garbage collection?
Are you referring to the case when multiple clients share a pre-shared master secret? In that case, I think that a rogue client can always DoS the other clients. For example, the rogue client may inject an OSCORE message with a high sequence number in the name of another client. Maybe, to defend against rogue clients, a better solution is to also use a different pre-shared master secret per client.
In my implementation, I derive AEAD keys on demand, which saves some RAM.
This could be CPU intensive, but understand why you may want to do that. I would prefer not to have to do this.
Besides, I do not copy the master keys, master salt, etc. to the endpoint / session structures. I just store application-provided pointers.
I have had too much experience of variables being allocated on a stack in some function call and then assumed to not be changing value over time - but the stack gets re-used. Code also needs to support future group OSCORE / EDHOC etc.
Additionally, you may pass the exact sizes when allocating RAM for the inner and outer PDUs.
I have removed coap_oscore_overhead() size addition when creating the inner PDU, as well the PDU size required in build_and_send_error_pdu().
Are you referring to the case when multiple clients share a pre-shared master secret? In that case, I think that a rogue client can always DoS the other clients. For example, the rogue client may inject an OSCORE message with a high sequence number in the name of another client. Maybe, to defend against rogue clients, a better solution is to also use a different pre-shared master secret per client.
I was more thinking about a rogue client gets hold of the pre-shared master secret and then firing off lots of requests with different KIDs - and hence generating lots of recipient contexts - which would need to have to be limited in count / timed out etc. if you do not have an agreed set of valid client KIDs.
Reverted out change https://github.com/obgm/libcoap/pull/764#discussion_r876338502 where the sequence number is updated every re-transmission. See discussion at core-wg/corrclar.
Added in a separate commit for interoperability tests as per https://core-wg.github.io/oscore/test-spec5.html
Still working through this one. Force-pushes do not make reviewer's life easier.
Apologies. Changes pushed this time as separate commits to be squashed in later.
There is a bunch of non-COSE, non-OSCORE stuff in the COSE header files. I would like to remove anything that is not OSCORE from this PR. I can point out specific parts in the next review.
Unnecessary stuff removed and changes pushed.
One more thing: There is a comment on #ifdef HAVE_OSCORE
vs. #if HAVE_OSCORE
: Please note that there is currently a third variant: #if defined(HAVE_OSCORE)
.