RFC 9031

Constrained Join Protocol (CoJP) for 6TiSCH

๐Ÿ“… published May 2021, from ietf

Part 1 of 12 ๐ŸŽ€

RFC 9031: Constrained Join Protocol (CoJP) for 6TiSCH ๐Ÿ’–

Okay so the title block is giving a full squad. RFC 9031 is titled Constrained Join Protocol (CoJP) for 6TiSCH. It was written by M. Vuฤiniฤ‡ (who edited it, at Inria), J. Simon (Analog Devices), K. Pister (University of California Berkeley), and M. Richardson (Sandelman Software Works). It's a Standards Track document from the IETF, dated May 2021, with ISSN 2070-1721. The title block doesn't list anything it obsoletes or updates. ๐Ÿฆ‹

Abstract โœจ

Okay so this doc is about the smallest possible setup a brand-new device, called a "pledge", needs in order to securely join a 6TiSCH network (that's IPv6 over the Time-Slotted Channel Hopping mode of IEEE 802.15.4) ๐ŸŒธ The pledge and the JRC (Join Registrar/Coordinator, a central entity) have to share a symmetric key. How that key gets provisioned is out of scope for this doc.

With one CoAP (Constrained Application Protocol) request-response exchange, secured by OSCORE (Object Security for Constrained RESTful Environments), the pledge asks to be admitted to the network, and the JRC hands it link-layer keying material plus other parameters. The JRC may update those parameters at any time through another OSCORE-secured request-response exchange. ๐Ÿ”ฅ

The spec defines the Constrained Join Protocol and its CBOR (Concise Binary Object Representation) data structures, and it explains how to configure the rest of the 6TiSCH communication stack so the join happens securely. Additional security mechanisms may be added on top of this minimal framework. ๐Ÿ’…


Status of This Memo ๐Ÿ“œ

This is an Internet Standards Track document. ๐Ÿ’œ

This document is a product of the Internet Engineering Task Force (IETF). It represents the consensus of the IETF community. It has had public review and has been approved for publication by the Internet Engineering Steering Group (IESG). More info on Internet Standards is in Section 2 of RFC 7841.

Info about the current status of this document, any errata, and how to send feedback is at https://www.rfc-editor.org/info/rfc9031. โœจ

Copyright (c) 2021 IETF Trust and the persons identified as the document authors. All rights reserved. ๐ŸŒˆ

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication. Please read these carefully, because they describe your rights and restrictions for this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions, and they are provided without warranty as described in the Simplified BSD License. ๐Ÿซถ


Table of Contents ๐Ÿ—‚๏ธ

1.  Introduction
2.  Terminology
3.  Provisioning Phase
4.  Join Process Overview
  4.1.  Step 1 - Enhanced Beacon
  4.2.  Step 2 - Neighbor Discovery
  4.3.  Step 3 - Constrained Join Protocol (CoJP) Execution
  4.4.  The Special Case of the 6LBR Pledge Joining
5.  Link-Layer Configuration
  5.1.  Distribution of Time
6.  Network-Layer Configuration
  6.1.  Identification of Unauthenticated Traffic
7.  Application-Layer Configuration
  7.1.  Statelessness of the JP
  7.2.  Recommended Settings
  7.3.  OSCORE
8.  Constrained Join Protocol (CoJP)
  8.1.  Join Exchange
  8.2.  Parameter Update Exchange
  8.3.  Error Handling
  8.4.  CoJP Objects
  8.5.  Recommended Settings
9.  Security Considerations
10. Privacy Considerations
11. IANA Considerations
  11.1.  Constrained Join Protocol (CoJP) Parameters
  11.2.  Constrained Join Protocol (CoJP) Key Usage
  11.3.  Constrained Join Protocol (CoJP) Unsupported Configuration
         Codes
12. References
  12.1.  Normative References
  12.2.  Informative References
Appendix A.  Example
Appendix B.  Lightweight Implementation Option
Acknowledgments
Authors' Addresses

1. Introduction ๐Ÿงš

This doc defines a "secure join" solution for a new device, the "pledge", so it can securely join a 6TiSCH network. "Secure join" is the term for network access authentication, authorization, and parameter distribution, as defined in [RFC9030]. CoJP (the Constrained Join Protocol defined here) handles the parameter distribution a pledge needs in order to become a joined node. Mutual authentication during network access and implicit authorization both come from a secure channel configured according to this doc. The doc also specifies how different layers of the 6TiSCH protocol stack should be configured so there's a smaller Denial of Service (DoS) attack surface during the join process. ๐ŸŽ€

This doc assumes a 6TiSCH network as described by [RFC7554] and [RFC8180]. By design, nodes in a 6TiSCH network [RFC7554] keep their radio turned off most of the time to save energy. That means the link a new device uses to join has limited bandwidth [RFC8180], so the secure join solution here keeps the number of over-the-air exchanges to a minimum. ๐Ÿ”‹

The microcontrollers inside 6TiSCH nodes have tiny amounts of code memory, so reusing existing protocols from the 6TiSCH stack is a must-do vibe ๐Ÿ’… At the application layer, the stack already uses CoAP [RFC7252] for web transfer and OSCORE [RFC8613] for end-to-end security. The secure join solution here reuses both of those protocols as its building blocks. ๐Ÿงฉ

CoJP is a generic protocol that can be used as-is in all modes of IEEE Std 802.15.4 [IEEE802.15.4], including the Time-Slotted Channel Hopping (TSCH) mode that 6TiSCH is based on. CoJP may also be used in other low-power networking technologies where communication overhead and code footprint really matter. In that case, companion documents may be needed to define the configuration parameters specific to that technology. The overall process is described in Section 4, and the stack configuration is specific to 6TiSCH. ๐ŸŒธ

CoJP assumes there's a Join Registrar/Coordinator (JRC), a central entity. The configuration here assumes that the pledge and the JRC share a unique symmetric cryptographic key, called the PSK (pre-shared key). The PSK configures OSCORE to give CoJP a secure channel. How the PSK gets installed is out of scope for this doc. It might happen during the provisioning phase, or through a key exchange protocol that runs before CoJP. ๐Ÿ”‘

When a pledge wants to join a 6TiSCH network, it first synchronizes to the network by starting the passive scan defined in [IEEE802.15.4]. Then the pledge exchanges CoJP messages with the JRC. For that end-to-end conversation to work, the messages get forwarded by nodes called Join Proxies, which are already part of the 6TiSCH network. These messages let the JRC and the pledge mutually authenticate, based on the properties OSCORE provides. They also let the JRC configure the pledge with link-layer keying material, a short identifier, and other parameters. Once this secure join process completes successfully, the joined node can talk to its neighbors to request extra bandwidth using the 6TiSCH Operation Sublayer (6top) Protocol [RFC8480], and it can start sending application traffic. ๐Ÿฆ‹

2. Terminology ๐Ÿ“–

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here. โœจ

The reader is expected to be familiar with the terms and concepts defined in [RFC9030], [RFC7252], [RFC8613], and [RFC8152]. ๐Ÿค“

The specification also includes a set of informative specifications using the Concise Data Definition Language (CDDL) [RFC8610]. ๐Ÿ’œ

These terms, defined in [RFC9030], get used a lot in this doc:

  • pledge
  • joined node
  • Join Proxy (JP)
  • Join Registrar/Coordinator (JRC)
  • Enhanced Beacon (EB)
  • join protocol
  • join process

These terms, defined in [RFC8505], are also used throughout:

  • 6LoWPAN Border Router (6LBR)
  • 6LoWPAN Node (6LN)

The term "6LBR" is used interchangeably with the term "DODAG root" defined in [RFC6550], on the assumption that the two entities are co-located, as recommended by [RFC9030]. ๐ŸŒˆ

The term "pledge", as used throughout the document, explicitly means non-6LBR devices trying to join the network using their IEEE Std 802.15.4 network interface. A device that tries to join as the 6LBR of the network, and does so over a different network interface, is explicitly called the "6LBR pledge". When the text applies equally to the pledge and the 6LBR pledge, the "(6LBR) pledge" form is used. ๐Ÿ“

We also use the generic terms "pledge identifier" and "network identifier". See Section 3. ๐Ÿซถ

3. Provisioning Phase โœจ

okay so before a (6LBR) pledge even tries to join, it needs some parameters loaded in, and the JRC gets the same ones ๐Ÿ’– There are lots of ways to do this. You can write them in with a JTAG (Joint Test Action Group) interface, use a serial (craft) console, push buttons on different devices at the same time, or configure over-the-air inside a Faraday cage ๐Ÿงš The vendor, the manufacturer, or the integrator can all be the one doing it, ngl it's giving teamwork.

How the provisioning actually happens is out of scope for this doc ๐Ÿ™ˆ The only assumption is that the JRC and the (6LBR) pledge can have a secure, private chat and swap those parameters ๐Ÿ”’

Here's what gets provisioned to the (6LBR) pledge:

  • pledge identifier: This identifies the (6LBR) pledge, and it MUST be unique across every pledge identifier that a JRC manages. This uniqueness is a big deal for security, as Section 9 covers ๐Ÿšจ It's usually the globally unique 64-bit Extended Unique Identifier (EUI-64) of the IEEE Std 802.15.4 device, so the hardware manufacturer provisions it. The pledge identifier is used to build the (6LBR) pledge's IPv6 addresses and to recognize it during the join protocol. Depending on the setup, it can also keep identifying the node after it has joined ๐Ÿฆ‹ For privacy reasons (see Section 10), you can use a pledge identifier that isn't the EUI-64. For example, it could be a random byte string, but you have to make sure that string is still unique, no cap ๐Ÿ’…
  • Pre-Shared Key (PSK): This is a symmetric cryptographic key that the (6LBR) pledge and the JRC share. To find the PSK for a given pledge, the JRC also needs to store the matching pledge identifier ๐Ÿ—๏ธ Every (6LBR) pledge MUST get its own unique PSK ๐Ÿ’œ The PSK MUST be cryptographically strong, with at least 128 bits of entropy, and it has to be indistinguishable by feasible computation from a random uniform string of the same length. Main character energy ๐Ÿ‘‘ How the PSK gets generated or provisioned is out of scope for this spec. It could happen during a provisioning step, or a companion document could specify a key-agreement protocol instead. The common mistakes people make when generating PSKs are covered in Section 9, so go read that ๐Ÿ™ˆ If you're recommissioning a device for a new owner, the PSK MUST be changed ๐Ÿ”„ Also, the PSK is NOT the same thing as the link-layer keys K1 and K2 from [RFC8180] ๐Ÿ’€ The PSK is a long-term secret that protects the secure join protocol in this document, and the link-layer keys are transported as part of that secure join protocol, so they're different things, iykyk โœจ
  • Optionally, a network identifier: This identifies the 6TiSCH network. The network identifier MUST be carried within Enhanced Beacon (EB) frames. Typically, the 16-bit Personal Area Network Identifier (PAN ID) defined in [IEEE802.15.4] gets used as the network identifier ๐ŸŒธ BUT the PAN ID isn't a stable network identifier, because it can change during the network's lifetime if a collision with another network gets detected ๐Ÿ˜ญ Companion documents can specify a different network identifier for join purposes, but that's out of scope here. Provisioning the network identifier to a pledge is RECOMMENDED ๐ŸŽ€ Still, due to operational constraints, the network identifier might not be known at provisioning time. If this parameter isn't provisioned to the pledge, the pledge will try to join one advertised network at a time, and that seriously drags out the join process ๐Ÿข This parameter MUST be provisioned to the 6LBR pledge. Non-negotiable ๐Ÿ’…
  • Optionally, any non-default algorithms: The default algorithms are in Section 7.3.3. If algorithm identifiers aren't provisioned, the defaults are implied ๐Ÿงš

Also, a 6LBR pledge that isn't co-located with the JRC needs these extra parameters:

  • Global IPv6 address of the JRC: The (6LBR) pledge uses this address to reach the JRC during the join process ๐ŸŒˆ It can also find the JRC's IPv6 address through other mechanisms, like DHCPv6 [RFC8415], the Generic Autonomic Signaling Protocol (GRASP) [RFC8990], or Multicast DNS (mDNS) [RFC6762]. Those mechanisms are out of scope for this document ๐Ÿ™ˆ Pledges don't need this address provisioned, though, because they discover it dynamically through CoJP โœจ

4. Join Process Overview ๐Ÿฆ‹

okay this section is the play-by-play of what a pledge does when it wants into a 6TiSCH network ๐Ÿ’… When a pledge wants admission, this exchange happens:

  1. The pledge listens for an Enhanced Beacon (EB) frame [IEEE802.15.4] ๐Ÿ“ก The frame gives it network synchronization info, like when it can send a frame to whoever's sending the beacons and when it can expect to receive one. That sender acts as a Join Proxy (JP) for the pledge ๐Ÿซถ The EB gives the JP's link-layer address, and it might also give the JP's link-local IPv6 address.
  2. The pledge configures its link-local IPv6 address and advertises it to the JP with Neighbor Discovery โœจ The advertisement step can be skipped if the link-local address was derived from a known unique interface identifier, like an EUI-64 address.
  3. The pledge sends a Join Request to the JP so it can securely identify itself to the network ๐Ÿ” The JP forwards that Join Request to the JRC.
  4. If the request gets processed successfully, the pledge gets a Join Response from the JRC (passed along by the JP) ๐ŸŽ‰ The Join Response carries the configuration parameters the pledge needs to join the network.

From the pledge's point of view, joining is super local ๐Ÿฅน It only talks to the JP, so it doesn't need to know how far away it is from the 6LBR or how to route to the JRC. It only needs the particulars of the 6TiSCH network after it has set up one or more link-layer keys ๐Ÿ’œ

Here's the transaction diagram for the join process, Figure 1 ๐ŸŽ€

  +--------+                 +-------+                 +--------+
  | pledge |                 |  JP   |                 |  JRC   |
  |        |                 |       |                 |        |
  +--------+                 +-------+                 +--------+
     |                          |                          |
     |<---Enhanced Beacon (1)---|                          |
     |                          |                          |
     |<-Neighbor Discovery (2)->|                          |
     |                          |                          |
     |-----Join Request (3a)----|----Join Request (3a)---->| \
     |                          |                          | | CoJP
     |<----Join Response (3b)---|----Join Response (3b)----| /
     |                          |                          |

Figure 1: Overview of a successful join process. ๐ŸŒธ

As for other nodes in the network, the 6LBR node can act as the JP ๐Ÿงš The 6LBR can also be co-located with the JRC, which is giving multitasking ๐Ÿ‘‘

The details of each step are in the sections below ๐Ÿ“–

4.1. Step 1 - Enhanced Beacon ๐Ÿ“ก

The pledge syncs to the network by listening for and receiving an EB that a node already in the network sent ๐Ÿฅฐ All of this is fully defined by [IEEE802.15.4] and described in [RFC7554].

Once the pledge hears an EB, it syncs to the joining schedule using the cells in that EB ๐Ÿฆ‹ The pledge can hear multiple EBs, and choosing which one to use is out of scope for this document. It's discussed in [RFC7554] ๐Ÿ’… Implementers should make use of info like this: which network identifier the EB has, the value of the Join Metric field in EBs, whether the EB's source link-layer address has been tried before, and the signal strength each EB arrived at, etc. The pledge can also be preconfigured to look for EBs with a specific network identifier ๐ŸŽฏ

If the pledge doesn't have a network identifier provisioned, it tries to join one network at a time, as described in Section 8.1.1 ๐Ÿ™ˆ

Once the pledge picks an EB, it syncs to it and drops into a low-power mode ๐Ÿ˜ด It follows the schedule in the EB, which shows the slots the pledge can use for the join process. While the rest of the join process is happening, the node that sent the EB acts as the JP ๐Ÿซถ

At this point, the pledge can either move on to step 2 or keep listening for more EBs โœจ

4.2. Step 2 - Neighbor Discovery ๐ŸŒˆ

The pledge builds its link-local IPv6 address from the interface identifier, per [RFC4944] ๐Ÿงš The pledge MAY do the Neighbor Solicitation / Neighbor Advertisement exchange with the JP, per Section 5.6 of [RFC8505] ๐Ÿ’œ Per [RFC8505], there's no need to run duplicate address detection for the link-local address, so skip that ๐Ÿ™Œ The pledge and the JP use their link-local IPv6 addresses for everything else in the join process.

Heads up: Neighbor Discovery exchanges at this stage aren't protected with link-layer security, because the pledge doesn't have the keys yet ๐Ÿ˜ญ How the JP accepts these unprotected frames is covered in Section 5 ๐Ÿ”

4.3. Step 3 - Constrained Join Protocol (CoJP) Execution ๐Ÿ”

The pledge kicks off the join exchange of the Constrained Join Protocol (CoJP) ๐ŸŽ€ The join exchange has two messages: the Join Request (Step 3a, Section 4.3.1) and the Join Response, which only happens if the security processing of the request succeeds (Step 3b, Section 4.3.2) โœจ

All CoJP messages go over a secure end-to-end channel that gives confidentiality, data authenticity, and replay protection ๐Ÿ›ก๏ธ Frames that carry CoJP messages aren't protected with link-layer security between the pledge and the JP, because the pledge doesn't have the link-layer keys in use yet ๐Ÿ™ˆ How the JP and pledge accept these unprotected frames is covered in Section 5. Once nodes that have already joined the network exchange frames carrying CoJP messages, link-layer security gets applied based on the network's security configuration ๐Ÿ”’

4.3.1. Step 3a - Join Request ๐Ÿ“จ

The Join Request is a message the pledge sends to the JP, and the JP forwards it to the JRC ๐Ÿ’Œ In the Join Request, the pledge says what role it wants to play in the network and the identifier of the network it wants to join ๐ŸŽฏ The JP forwards the Join Request to the JRC over the existing links. How that actually happens is out of scope for this document, but some networks might want to set aside specific link-layer resources for this join traffic ๐Ÿฆ‹

4.3.2. Step 3b - Join Response ๐Ÿ’Œ

okay so the Join Response is the JRC's reply to the pledge, and it gets forwarded through the JP ๐Ÿฆ‹. The packet travels from the JRC to the JP over the operating routes in the network, and then the JP delivers it to the pledge. The JP is an application-layer proxy here, so check Section 7 for the deets ๐Ÿซถ

The Join Response is packed with everything the pledge needs to become a fully operational network node ๐Ÿ’… That includes the link-layer key(s) currently in use in the network, the short address assigned to the pledge, and the IPv6 address of the JRC (which the pledge needs so it can operate as a JP later), plus some other bits โœจ

4.4. The Special Case of the 6LBR Pledge Joining ๐Ÿงš

the 6LBR pledge does Section 4.3 of the join process just like any other pledge, just over a different network interface ๐Ÿ”Œ There's no JP in the middle between the 6LBR pledge and the JRC, as described in Section 6 ๐Ÿ™… The other steps of the join process don't apply to the 6LBR pledge. How the 6LBR pledge gets an IPv6 address and kicks off CoJP is out of scope for this document ๐Ÿคทโ€โ™€๏ธ

okay the big thing in an operational 6TiSCH network is that all frames use link-layer frame security [RFC8180] ๐Ÿ” The IEEE Std 802.15.4 security attributes include frame authenticity and, optionally, frame confidentiality (aka encryption) ๐Ÿ’œ

Any node sending EB frames MUST be prepared to act as a JP for potential pledges ๐Ÿ“

The pledge doesn't do an authenticity check on EB frames at first, because it doesn't have the link-layer key(s) in use yet ๐Ÿ™ˆ It can still parse the contents of the EBs and sync to the network, since EBs aren't encrypted [RFC8180] โœจ

When the pledge sends frames during the join, it sends them unencrypted and unauthenticated at the link layer ๐Ÿซ  So for the join to even be possible, the JP has to accept these unsecured frames for the whole duration of the join ๐Ÿšช This can be done by setting the secExempt attribute in the IEEE Std 802.15.4 security configuration tables. The lower layer is expected to give the upper layer a way to tell that unsecured frames are coming in from a device. The upper layer can use that info to figure out that a join is in progress and that the unsecured frames should be processed. How the JP makes that call and talks to the lower layer is out of scope for this spec ๐Ÿ™… The JP can also look at stuff like the value of the join rate parameter (Section 8.4.2) set by the JRC, a physical button press, etc ๐Ÿ”˜

When the pledge first syncs with the network, it has no means of verifying the authenticity of EB frames. Because an attacker can craft a frame that looks like a legit EB frame, this opens up a DoS vector, as discussed in Section 9 ๐Ÿ˜ฑ

heads up bestie: this is the sneaky part. Anyone can forge something that looks like a real EB, so the pledge can't just trust what it hears during that first sync. Read Section 9 ๐Ÿซถ

5.1. Distribution of Time โฐ

Nodes in a 6TiSCH network keep a global notion of time known as the Absolute Slot Number โœจ It's used in building the link-layer nonce, as defined in [IEEE802.15.4]. The pledge first syncs with the EB frame sent by the JP and takes the Absolute Slot Number value from the TSCH Synchronization Information Element. At the moment of sync, the EB frame can neither be authenticated nor have its freshness verified ๐Ÿฅบ During the join, the pledge sends frames that are unprotected at the link layer but protected end-to-end instead. The pledge doesn't get the time info as the output of the join process, because that info is local to the network and may not be known at the JRC โณ

This enables an attack on the pledge ๐Ÿ˜ค The attacker replays legit EB frames obtained from the network to the pledge and acts as a man-in-the-middle between the pledge and the JP. Those EB frames make the pledge believe the replayed Absolute Slot Number value is the current notion of time in the network. By forwarding the join traffic to the legitimate JP, the attacker lets the pledge join the network. Depending on conditions around the JRC reusing the pledge's short address or the pledge trying to rejoin, this may cause the pledge to reuse the link-layer nonce in the first frame it sends protected after the join process is completed ๐Ÿ’€

For this reason, all frames originated at the JP and destined to the pledge during the join process MUST be authenticated at the link layer using the key that is normally in use in the network ๐Ÿ”‘ Link-layer security processing at the pledge for these frames will fail, because the pledge doesn't have the key yet ๐Ÿ™ƒ The pledge acknowledges these frames without link-layer security, and the JP accepts the unsecured acknowledgment thanks to the secExempt attribute set for the pledge. The frames should be passed to the upper layer for processing using the promiscuous mode of [IEEE802.15.4] or another appropriate mechanism ๐Ÿซง When the upper-layer processing on the pledge is completed and the link-layer keys are configured, the upper layer MUST trigger the security processing of the corresponding frame ๐Ÿ” Once the security processing of the frame carrying the Join Response message is successful, the current Absolute Slot Number kept locally at the pledge SHALL be declared as valid ๐Ÿ‘‘

6. Network-Layer Configuration ๐ŸŒ

The pledge and the JP SHOULD keep a separate neighbor cache for untrusted entries, and use it to store each other's information during the join process ๐Ÿ—‚๏ธ Mixing neighbor entries belonging to pledges with nodes that are part of the network opens up the JP to a DoS attack, because the attacker can fill the JP's neighbor table and block the discovery of legitimate neighbors ๐Ÿ˜ฌ

Once the pledge obtains link-layer keys and becomes a joined node, it can securely communicate with its neighbors, obtain the network IPv6 prefix, and form its global IPv6 address ๐ŸŽ‰ The joined node then goes through an independent process to bootstrap its neighbor cache entries, possibly with a node that formerly acted as a JP, following [RFC8505]. From the point of view of the JP, there's no relationship between the neighbor cache entry belonging to a pledge and the joined node that formerly acted as a pledge ๐Ÿ‘€

The pledge doesn't communicate with the JRC at the network layer ๐Ÿ™… This lets the pledge join without knowing the IPv6 address of the JRC. Instead, the pledge talks to the JP at the network layer using link-local addressing, and talks to the JRC at the application layer, as specified in Section 7 ๐ŸŽ€

The JP communicates with the JRC over global IPv6 addresses ๐ŸŒ The JP discovers the network IPv6 prefix and configures its global IPv6 address upon successful completion of the join process and the obtention of link-layer keys. The pledge learns the IPv6 address of the JRC from the Join Response, as specified in Section 8.1.2, and uses it once joined in order to operate as a JP ๐Ÿฆ‹

As a special case, the 6LBR pledge may have an additional network interface that it uses to obtain configuration parameters from the JRC and to start advertising the 6TiSCH network ๐Ÿ“ก This additional interface needs to be configured with a global IPv6 address, by a mechanism that is out of scope of this document. The 6LBR pledge uses this interface to directly communicate with the JRC using global IPv6 addressing ๐Ÿ’œ

The JRC can be co-located on the 6LBR ๐Ÿ  In this special case, the IPv6 address of the JRC can be omitted from the Join Response message for space optimization ๐Ÿชถ The 6LBR then MUST set the DODAGID field in the RPL DODAG Information Objects (DIOs) [RFC6550] to its IPv6 address. The pledge learns the address of the JRC once it's joined and upon receiving the first RPL DIO message, and then uses it to operate as a JP ๐ŸŒธ

6.1. Identification of Unauthenticated Traffic ๐Ÿ•ต๏ธโ€โ™€๏ธ

The traffic proxied by the JP comes from unauthenticated pledges, and there may be an arbitrary amount of it ๐Ÿ“ฆ In particular, an attacker may send fraudulent traffic in an attempt to overwhelm the network ๐ŸŒช๏ธ

When operating as part of a 6TiSCH minimal network [RFC8180] using distributed scheduling algorithms, the traffic from unauthenticated pledges may cause intermediate nodes to request additional bandwidth. An attacker could use this property to cause the network to overcommit bandwidth (and energy) to the join process ๐Ÿ˜ค

The JP is aware of what traffic originates from unauthenticated pledges, so it can avoid allocating additional bandwidth itself ๐Ÿง  The JP implements a data cap on outgoing join traffic by implementing the recommendation of 1 packet per 3 seconds in Section 3.1.3 of [RFC8085]. This can be achieved with the congestion control mechanism specified in Section 4.7 of [RFC7252]. This cap will not protect intermediate nodes, because they cannot tell join traffic from regular traffic ๐Ÿ™ˆ Despite the data cap implemented separately on each JP, the aggregate join traffic from many JPs may cause intermediate nodes to decide to allocate additional cells. It's undesirable to do so in response to traffic originated from unauthenticated pledges ๐Ÿšซ To let intermediate nodes avoid this, the traffic needs to be tagged ๐Ÿท๏ธ [RFC2597] defines a set of per-hop behaviors that may be encoded into the Diffserv Code Points (DSCPs). Based on the DSCP, intermediate nodes can decide whether to act on a given packet โœจ

6.1.1. Traffic from JP to JRC ๐Ÿ“ค

The JP SHOULD set the DSCP of packets that it produces as part of the forwarding process to the AF43 code point (See Section 6 of [RFC2597]). A JP that does not require a specific DSCP value on forwarded traffic should set it to zero so that it is compressed out ๐Ÿ—œ๏ธ

A Scheduling Function (SF) running on 6TiSCH nodes SHOULD NOT allocate additional cells as a result of traffic with code point AF43 ๐Ÿ™…โ€โ™€๏ธ Companion SF documents SHOULD specify how this recommended behavior is achieved ๐Ÿ“š

6.1.2. Traffic from JRC to JP ๐Ÿ“ฅ

The JRC SHOULD set the DSCP of Join Response packets addressed to the JP to the AF42 code point ๐Ÿ’… AF42 has a lower drop probability than AF43, giving this traffic priority in buffers over the traffic going towards the JRC ๐Ÿ‘‘

The 6LBR links are often the most congested within a DODAG, and from that point down, there is progressively less (or equal) congestion ๐Ÿ“‰ If the 6LBR paces itself when sending Join Response traffic, then it ought to never exceed the bandwidth allocated to the best effort traffic cells. If the 6LBR has the capacity (if it is not constrained), then it should provide some buffers in order to satisfy the Assured Forwarding behavior ๐Ÿซง

Companion SF documents SHOULD specify how traffic with code point AF42 is handled with respect to cell allocation ๐Ÿ“– If the recommended behavior described in this section is not followed, the network may become prone to the attack discussed in Section 6.1. ๐Ÿ’€

7. Application-Layer Configuration ๐ŸŽ€

okay so the CoJP join exchange in Figure 1 rides on CoAP [RFC7252] and gets its secure channel from OSCORE [RFC8613] ๐Ÿ” the (6LBR) pledge is the CoAP client and the JRC is the CoAP server ๐Ÿ’– the JP implements CoAP forward proxy functionality [RFC7252], and since the JP can also be a constrained device, it cannot implement a cache ๐Ÿ™ˆ

the pledge tells the JP it wants it as a proxy by including the Proxy-Scheme option in the CoAP requests it sends to the JP โœจ it also adds the Uri-Host option, with its value set to the well-known JRC's alias from Section 8.1.1 ๐Ÿ’Œ

the JP resolves that alias to the IPv6 address of the JRC it learned when it acted as a pledge and joined the network ๐Ÿฆ‹ this is what lets the JP reach the JRC at the network layer and forward the requests on behalf of the pledge. main character energy for the JP ๐Ÿ‘‘

7.1. Statelessness of the JP

the CoAP proxy from [RFC7252] keeps per-client state so it can send the response back toward whoever originally asked ๐Ÿง  that state includes at least the CoAP token, the IPv6 address of the client, and the UDP source port number. and bestie, since the JP can be a constrained device that acts as a CoAP proxy, its memory is limited, which makes it prone to a DoS attack ๐Ÿ˜ญ

this DoS vector on the JP can be mitigated by making the JP act as a stateless CoAP proxy ๐Ÿ›ก๏ธ here "state" means all the info tied to individual pledges. the JP can wrap the state it needs for a given pledge throughout the network stack in a "state object" and include it as a CoAP token in the forwarded request to the JRC ๐ŸŽ the JP MAY use the CoAP token as defined in [RFC7252] if the size of the serialized state object permits, or use the extended CoAP token defined in [RFC8974] to carry the state object. the JRC and any other potential proxy on the JP-JRC path MUST support extended token lengths, as defined in [RFC8974] ๐Ÿงฉ since the CoAP token gets echoed back in the response, the JP can decode the state object and set up what it needs to forward the response to the pledge ๐Ÿ” what the JP has to encode in the state object to work fully statelessly for a given pledge is implementation specific ๐ŸŒธ

It is RECOMMENDED that the JP operates in a stateless manner and signals the per-pledge state within the CoAP token for every request that it forwards into the network on behalf of unauthenticated pledges ๐Ÿ™Œ when the JP is stateless, the security considerations from [RFC8974] apply, and the type of CoAP message that the JP forwards on behalf of the pledge MUST be non-confirmable (NON), no matter what message type it received from the pledge ๐Ÿšซ using a non-confirmable message means the JP doesn't have to keep CoAP message exchange state, so the retransmission burden is entirely shifted to the pledge ๐Ÿซถ a JP that operates statelessly still needs to keep congestion control state with the JRC, see Section 9 ๐Ÿ“‰ recommended CoAP settings for the join process, for both the pledge and the JP, are in Section 7.2.

ngl, a fully (per-pledge) stateless JP can be tricky to build in some networking stack implementations ๐Ÿงต in those cases, the JP may act as a stateful proxy that stores the per-pledge state until the response arrives or times out. but yep, that comes at the price of a DoS vector ๐Ÿ’€

this section gives RECOMMENDED values of CoAP settings during the join process ๐Ÿ“‹

                   +===================+===============+
                   | Name              | Default Value |
                   +===================+===============+
                   | ACK_TIMEOUT       | 10 seconds    |
                   +-------------------+---------------+
                   | ACK_RANDOM_FACTOR | 1.5           |
                   +-------------------+---------------+
                   | MAX_RETRANSMIT    | 4             |
                   +-------------------+---------------+
                   | NSTART            | 1             |
                   +-------------------+---------------+
                   | DEFAULT_LEISURE   | 5 seconds     |
                   +-------------------+---------------+
                   | PROBING_RATE      | 1 byte/second |
                   +-------------------+---------------+

                    Table 1: Recommended CoAP settings.

these values can be configured to suit the deployment ๐ŸŽฏ the defaults were picked to work across a wide range of deployments, including dense networks ๐Ÿ™๏ธ

the PROBING_RATE at the JP is controlled by the join rate parameter, see Section 8.4.2 ๐Ÿ’ซ following [RFC7252], the average data rate when sending to the JRC must not exceed PROBING_RATE. for security reasons, the average data rate SHOULD be measured over a rather short window, e.g., ACK_TIMEOUT, see Section 9 โฑ๏ธ

7.3. OSCORE

before the (6LBR) pledge and the JRC start swapping CoAP messages protected with OSCORE, they need to derive the OSCORE security context from the provisioned parameters, as discussed in Section 3 ๐Ÿ”‘

the OSCORE security context MUST be derived per Section 3 of [RFC8613] ๐Ÿงช

  • The Master Secret MUST be the PSK. ๐Ÿ—๏ธ
  • The Master Salt MUST be the empty byte string. ๐Ÿซง
  • The ID Context MUST be set to the pledge identifier. ๐Ÿชช
  • The ID of the pledge MUST be set to the empty byte string. This identifier is used as the OSCORE Sender ID of the pledge in the security context derivation, since the pledge initially acts as a CoAP client. ๐Ÿ™‹โ€โ™€๏ธ
  • The ID of the JRC MUST be set to the byte string 0x4a5243 ("JRC" in ASCII). This identifier is used as the OSCORE Recipient ID of the pledge in the security context derivation, as the JRC initially acts as a CoAP server. ๐Ÿงš
  • The Algorithm MUST be set to the value from [RFC8152], agreed to out-of-band by the same mechanism used to provision the PSK. The default is AES-CCM-16-64-128. ๐ŸŽ€
  • The key derivation function MUST be agreed out-of-band by the same mechanism used to provision the PSK. Default is HKDF SHA-256 [RFC5869]. ๐ŸŒˆ

since the pledge's OSCORE Sender ID is the empty byte string, when building the OSCORE option the pledge sets the kid flag in the OSCORE flag bits but indicates a 0-length kid ๐Ÿค“ the pledge carries its pledge identifier in the kid context field of the OSCORE option. the derivation in [RFC8613] gives OSCORE keys and a Common Initialization Vector (IV) for each side of the conversation ๐ŸŽถ nonces are built by XORing the Common IV with the current sequence number. for the nitty-gritty on nonce and OSCORE option construction, check [RFC8613] ๐Ÿ“–

Implementations MUST ensure that multiple CoAP requests, including ones to different JRCs, properly increment the sequence numbers, so the same sequence number is never reused in distinct requests protected under the same PSK ๐Ÿšจ the pledge typically sends requests to different JRCs if it isn't provisioned with the network identifier and tries to join one network at a time. if they don't comply, the AEAD algorithm's security guarantees break because of nonce reuse ๐Ÿ’”

this OSCORE security context is used for the initial joining of the (6LBR) pledge, where the pledge acts as a CoAP client, and also for any later parameter updates, where the JRC acts as a CoAP client and the joined node acts as a CoAP server, as discussed in Section 8.2 ๐Ÿ”„ when the pledge and the JRC swap roles between CoAP client and CoAP server, the same OSCORE security context as initially derived stays in use, and the derived parameters don't change (like the Sender ID when sending and the Recipient ID when receiving, see Section 3.1 of [RFC8613]) ๐Ÿงท a (6LBR) pledge is expected to have exactly one OSCORE security context with the JRC ๐Ÿ’…

7.3.1. Replay Window and Persistency

both the (6LBR) pledge and the JRC MUST implement a replay-protection mechanism ๐Ÿ›‘ using the default OSCORE replay-protection mechanism from Section 3.2.2 of [RFC8613] is RECOMMENDED ๐Ÿ‘

Implementations MUST ensure that mutable OSCORE context parameters (Sender Sequence Number, Replay Window) are stored in persistent memory ๐Ÿ’พ a technique from Appendix B.1.1 of [RFC8613] that prevents reuse of sequence numbers MUST be implemented. every update to the OSCORE Replay Window MUST be written to persistent memory โœ๏ธ

this is a big deal bestie. it's how you guarantee nonce uniqueness and resistance to replay attacks across reboots and rejoins ๐Ÿšจ

traffic between the (6LBR) pledge and the JRC is rare, so security outweighs the cost of writing to persistent memory ๐Ÿซถ

7.3.2. OSCORE Error Handling

Errors raised by OSCORE during the join process MUST be silently dropped, with no error response being signaled ๐Ÿคซ the pledge MUST silently discard any response that isn't protected with OSCORE, including error codes ๐Ÿ™…โ€โ™€๏ธ

these errors can happen for a bunch of reasons, like a failed lookup of an appropriate security context (for example, the pledge trying to join the wrong network), failed decryption, a positive Replay Window lookup, or formatting errors (maybe from malicious alterations in transit) ๐Ÿ‘€ silently dropping OSCORE messages blocks a DoS attack on the pledge, where an attacker could send bogus error responses to force the pledge to try joining one network at a time until every network has been tried ๐Ÿ›ก๏ธ๐Ÿ’€

7.3.3. Mandatory-to-Implement Algorithms

the mandatory-to-implement AEAD algorithm for OSCORE is AES-CCM-16-64-128 from [RFC8152] ๐Ÿ” it's the algorithm used for securing IEEE Std 802.15.4 frames, and hardware acceleration for it is present in virtually all compliant radio chips ๐Ÿ“ก with this choice, CoAP messages are protected with an 8-byte CCM authentication tag, and the algorithm uses 13-byte long nonces ๐Ÿง

the mandatory-to-implement hash algorithm is SHA-256 [RFC4231] ๐ŸŒธ the mandatory-to-implement key derivation function is HKDF [RFC5869], instantiated with a SHA-256 hash โœจ see Appendix B for implementation guidance when code footprint is important ๐Ÿง 

8. Constrained Join Protocol (CoJP) โœจ

okay bestie, CoJP is a lightweight protocol that runs over CoAP [RFC7252] and a secure channel from OSCORE [RFC8613] ๐Ÿ’… It lets a (6LBR) pledge ask to get into a network that the JRC manages ๐Ÿฆ‹ Then the JRC hands the pledge the parameters it needs. The JRC can also update those parameters later, by reaching out to the joined node that used to be a (6LBR) pledge. For example, network-wide rekeying happens by updating the keying material on each node ๐Ÿ”‘

CoJP leans on OSCORE's security properties, and it's iconic ๐Ÿซถ That means end-to-end confidentiality, data authenticity, replay protection, and a secure binding of responses to requests.

           +-----------------------------------+
           |  Constrained Join Protocol (CoJP) |
           +-----------------------------------+
           +-----------------------------------+  \
           |         Requests / Responses      |  |
           |-----------------------------------|  |
           |               OSCORE              |  | CoAP
           |-----------------------------------|  |
           |           Messaging Layer         |  |
           +-----------------------------------+  /
           +-----------------------------------+
           |                UDP                |
           +-----------------------------------+

Figure 2: Abstract layering of CoJP.

When a (6LBR) pledge requests admission to a given network, it goes through the CoJP join exchange, which has two parts:

  • The Join Request message, sent by the (6LBR) pledge to the JRC, potentially proxied by the JP. The Join Request message and its mapping to CoAP is specified in Section 8.1.1.
  • The Join Response message, sent by the JRC to the (6LBR) pledge, if the JRC successfully processes the Join Request using OSCORE and it determines through a mechanism that is out of scope of this specification that the (6LBR) pledge is authorized to join the network. The Join Response message is potentially proxied by the JP. The Join Response message and its mapping to CoAP is specified in Section 8.1.2.

When the JRC needs to update the parameters of a joined node that formerly acted as a (6LBR) pledge, it runs the CoJP parameter update exchange, which is just one message:

  • The Parameter Update message, sent by the JRC to the joined node that formerly acted as a (6LBR) pledge. The Parameter Update message and its mapping to CoAP is specified in Section 8.2.1.

The payload of CoJP messages is encoded with CBOR [RFC8949]. The CBOR data structures that may appear as the payload of different CoJP messages are specified in Section 8.4.

8.1. Join Exchange

This section specifies the messages exchanged when the (6LBR) pledge requests admission and configuration parameters from the JRC.

8.1.1. Join Request Message

The Join Request message that the (6LBR) pledge sends SHALL be mapped to a CoAP request:

  • The request method is POST.
  • The type is Confirmable (CON).
  • The Proxy-Scheme option is set to "coap".
  • The Uri-Host option is set to "6tisch.arpa". This is an anycast type of identifier of the JRC that is resolved to its IPv6 address by the JP or the 6LBR pledge.
  • The Uri-Path option is set to "j".
  • The OSCORE option SHALL be set according to [RFC8613]. The OSCORE security context used is the one derived in Section 7.3. The OSCORE 'kid context' allows the JRC to retrieve the security context for a given pledge.
  • The payload is a Join_Request CBOR object, as defined in Section 8.4.1.

Since the Join Request is a confirmable message, the transmission at (6LBR) pledge will be controlled by CoAP's retransmission mechanism. The JP, when operating in a stateless manner, forwards this Join Request as a non-confirmable (NON) CoAP message, as specified in Section 7. If the CoAP implementation at the (6LBR) pledge declares the message transmission a failure, the (6LBR) pledge SHOULD attempt to join a 6TiSCH network advertised with a different network identifier. See Section 7.2 for recommended values of CoAP settings to use during the join exchange.

If all join attempts to advertised networks have failed, the (6LBR) pledge SHOULD signal the presence of an error condition, through some out-of-band mechanism.

BCP 190 [RFC8820] provides guidelines on URI design and ownership. It recommends that whenever a third party wants to mandate a URI to web authority that it SHOULD go under "/.well-known" (per [RFC8615]). In the case of CoJP, the Uri-Host option is always set to "6tisch.arpa", and based upon the recommendations in Section 1 of [RFC8820], it is asserted that this document is the owner of the CoJP service. As such, the concerns of [RFC8820] do not apply, and thus the Uri-Path is only "j".

8.1.2. Join Response Message

The Join Response message that the JRC sends SHALL be mapped to a CoAP response:

  • The Response Code is 2.04 (Changed).
  • The payload is a Configuration CBOR object, as defined in Section 8.4.2.

8.2. Parameter Update Exchange

During the network lifetime, parameters returned as part of the Join Response may need to be updated. One typical example is the update of link-layer keying material for the network, a process known as rekeying. This section specifies a generic mechanism when this parameter update is initiated by the JRC.

At the time of the join, the (6LBR) pledge acts as a CoAP client and requests the network parameters through a representation of the "/j" resource exposed by the JRC. In order for the update of these parameters to happen, the JRC needs to asynchronously contact the joined node. The use of the CoAP Observe option for this purpose is not feasible due to the change in the IPv6 address when the pledge becomes the joined node and obtains a global address.

Instead, once the (6LBR) pledge receives and successfully validates the Join Response and so becomes a joined node, it becomes a CoAP server. The joined node creates a CoAP service at the Uri-Host value of "6tisch.arpa", and the joined node exposes the "/j" resource that is used by the JRC to update the parameters. Consequently, the JRC operates as a CoAP client when updating the parameters. The request/response exchange between the JRC and the (6LBR) pledge happens over the already-established OSCORE secure channel.

8.2.1. Parameter Update Message

The Parameter Update message that the JRC sends to the joined node SHALL be mapped to a CoAP request:

  • The request method is POST.
  • The type is Confirmable (CON).
  • The Uri-Host option is set to "6tisch.arpa".
  • The Uri-Path option is set to "j".
  • The OSCORE option SHALL be set according to [RFC8613]. The OSCORE security context used is the one derived in Section 7.3. When a joined node receives a request with the Sender ID set to 0x4a5243 (ID of the JRC), it is able to correctly retrieve the security context with the JRC.
  • The payload is a Configuration CBOR object, as defined in Section 8.4.2.

The JRC has implicit knowledge of the global IPv6 address of the joined node, as it knows the pledge identifier that the joined node used when it acted as a pledge and the IPv6 network prefix. The JRC uses this implicitly derived IPv6 address of the joined node to directly address CoAP messages to it.

If the JRC does not receive a response to a Parameter Update message, it attempts multiple retransmissions as configured by the underlying CoAP retransmission mechanism triggered for confirmable messages. Finally, if the CoAP implementation declares the transmission a failure, the JRC may consider this as a hint that the joined node is no longer in the network. How the JRC decides when to stop attempting to contact a previously joined node is out of scope of this specification, but the security considerations on the reuse of assigned resources apply, as discussed in Section 9.

8.3. Error Handling

8.3.1. CoJP CBOR Object Processing

CoJP CBOR objects are transported within both CoAP requests and responses. This section describes handling the cases in which certain CoJP CBOR object parameters are not supported by the implementation or their processing fails. See Section 7.3.2 for the handling of errors that may be raised by the underlying OSCORE implementation.

When such a parameter is detected in a CoAP request (Join Request message, Parameter Update message), a Diagnostic Response message MUST be returned. A Diagnostic Response message maps to a CoAP response and is specified in Section 8.3.2.

When a parameter that cannot be acted upon is encountered while processing a CoJP object in a CoAP response (Join Response message), a (6LBR) pledge SHOULD reattempt to join. In this case, the (6LBR) pledge SHOULD include the Unsupported Configuration CBOR object within the Join Request object in the following Join Request message. The Unsupported Configuration CBOR object is self-contained and enables the (6LBR) pledge to signal any parameters that the implementation of the networking stack may not support. A (6LBR) pledge MUST NOT attempt more than COJP_MAX_JOIN_ATTEMPTS number of attempts to join if the processing of the Join Response message fails each time. If the COJP_MAX_JOIN_ATTEMPTS number of attempts is reached without success, the (6LBR) pledge SHOULD signal the presence of an error condition through some out-of-band mechanism.

Note that COJP_MAX_JOIN_ATTEMPTS relates to the application-layer handling of the CoAP response and is different from CoAP's MAX_RETRANSMIT setting, which drives the retransmission mechanism of the underlying CoAP message.

8.3.2. Diagnostic Response Message

The Diagnostic Response message is returned for any CoJP request when the processing of the payload failed. The Diagnostic Response message is protected by OSCORE as any other CoJP message.

The Diagnostic Response message SHALL be mapped to a CoAP response:

  • The Response Code is 4.00 (Bad Request).
  • The payload is an Unsupported Configuration CBOR object, as defined in Section 8.4.5, containing more information about the parameter that triggered the sending of this message.

8.3.3. Failure Handling โœจ

okay so the parameter update exchange can happen literally any time during the network's life, and that can span several YEARS ๐Ÿ˜ญ. Over that long, stuff can go sideways. A joined node or the JRC might reboot or straight up die ๐Ÿ’€.

This document says the mutable parameters in the security context have to get written to persistent memory (see Section 7.3.1) by both the JRC and the pledges (joined nodes) ๐Ÿซถ. Pledges are usually little constrained devices that handle those writes in a predictable way, so their mutable security-context parameters can be retrieved after a reboot. That means no risk of AEAD nonce reuse from reinitialized Sender Sequence Numbers, and no replay attack from a reinitialized Replay Window ๐Ÿ›ก๏ธ. The JRC is a different story. It may be running on a generic machine, where writes to persistent memory can get unpredictable delays because of caching ๐Ÿ™ˆ. If the JRC reboots before that cached data is written out, the mutable security-context parameters are likely lost, and that poses the risk of AEAD nonce reuse ๐Ÿ˜ฑ.

If a complete device failure happens and the mutable security-context parameters can't be retrieved, it's expected that the failed joined node gets replaced with a brand new physical device, using a new pledge identifier and a PSK ๐Ÿ”. When the failure hits the JRC, the static info on provisioned pledges, like PSKs and pledge identifiers, might be recoverable through backups ๐Ÿ’พ. But the info about joined nodes (their assigned short identifiers and mutable security-context parameters) is likely gone. If that's the case, the network administrator MUST force all the networks managed by the failed JRC to rejoin through out-of-band means during the JRC reinitialization. For example, reinitialize the 6LBR nodes and freshly generate dynamic cryptographic keys and other parameters that influence the security properties of the network ๐Ÿ”‘.

Yes, this is the scary part bestie. A failed JRC with lost joined-node info means the whole network has to rejoin the hard way. ๐Ÿ˜ญ

To recover from that kind of failure, the reinitialized JRC can kick off a renegotiation of the OSCORE security context using the procedure in Appendix B.2 of [RFC8613] ๐Ÿ”„. Since it knows about the failure, the reinitialized JRC answers the first Join Request from each pledge it manages with a 4.01 (Unauthorized) error and a random nonce ๐ŸŽฒ. The pledge checks that error response, then starts the CoJP join exchange with a new OSCORE security context derived from an ID Context. That ID Context is the concatenation of two nonces: one the pledge got from the JRC, and one the pledge generated locally ๐Ÿงฉ. After the JRC verifies the Join Request with the new ID Context and the derived OSCORE security context, the JRC should consequently map the new ID Context to the previously used pledge identifier ๐Ÿ—บ๏ธ. How the JRC handles that mapping is out of scope for this document.

Using the procedure in Appendix B.2 of [RFC8613] is RECOMMENDED for handling these failure events, or any other event that could lead to the loss of mutable security-context parameters ๐Ÿ’…. The nonces exchanged using this procedure MUST be at least 8 bytes long ๐Ÿ“.

The procedure needs both the pledge and the JRC to have good sources of randomness ๐ŸŽฐ. That's usually not a problem on the JRC side, but the constrained device hosting the pledge might have limitations here ๐Ÿฅบ. If the pledge doesn't support the procedure outlined in Appendix B.2 of [RFC8613], the network administrator MUST reprovision the concerned devices with freshly generated parameters through out-of-band means ๐Ÿงพ.


8.4. CoJP Objects ๐Ÿงš

This section lays out the structure of the CoJP CBOR objects that can be carried as the payload of CoJP messages ๐ŸŽ€. Some of these objects can be received either during the CoJP join exchange, when the device is operating as a (CoJP) pledge, or during the parameter update exchange, when the device is operating as a joined (6LBR) node ๐Ÿ’œ.

8.4.1. Join Request Object ๐ŸŒธ

The Join_Request structure is built on a CBOR map object ๐Ÿ—บ๏ธ.

The parameters that can show up in a Join_Request object are summarized below. The labels can be found in the "Constrained Join Protocol (CoJP) Parameters" registry, Section 11.1 ๐Ÿ“š.

  • role: The identifier of the role the pledge wants to play in the network once it joins, encoded as an unsigned integer ๐Ÿ‘‘. The possible values are in Table 3. This parameter MAY be included. If it's omitted, the default value of 0, i.e., the role "6TiSCH Node", MUST be assumed ๐Ÿ“.
  • network identifier: The identifier of the network, as discussed in Section 3, encoded as a CBOR byte string ๐ŸŒ. When it's present in the Join_Request, it hints to the JRC which network the pledge wants to join, which lets the JRC manage multiple networks ๐Ÿฆ‹. The pledge gets the value of the network identifier from the received EB frames. This parameter MUST be included in a Join_Request object no matter what the role parameter value is ๐Ÿ’–.
  • unsupported configuration: The identifier of the parameters that the implementation doesn't support, encoded as an Unsupported_Configuration object, described in Section 8.4.5 ๐Ÿ™ˆ. This parameter MAY be included. If a (6LBR) pledge previously tried to join and got a valid Join Response message over OSCORE but couldn't act on its payload (Configuration object), it SHOULD include this parameter to help with recovery and debugging ๐Ÿ”.

Table 2 sums up the parameters that may appear in a Join_Request object ๐Ÿ“‹.

      +===========================+=======+==================+
      | Name                      | Label | CBOR Type        |
      +===========================+=======+==================+
      | role                      | 1     | unsigned integer |
      +---------------------------+-------+------------------+
      | network identifier        | 5     | byte string      |
      +---------------------------+-------+------------------+
      | unsupported configuration | 8     | array            |
      +---------------------------+-------+------------------+

            Table 2: Summary of Join_Request parameters.

Here's the CDDL fragment that represents the text above for the Join_Request ๐Ÿง:

Join_Request = {
    ? 1 : uint,                       ; role
      5 : bstr,                       ; network identifier
    ? 8 : Unsupported_Configuration   ; unsupported configuration
}
    +========+=======+==============================+===========+
    | Name   | Value | Description                  | Reference |
    +========+=======+==============================+===========+
    | 6TiSCH | 0     | The pledge requests to play  | RFC 9031  |
    | Node   |       | the role of a regular 6TiSCH |           |
    |        |       | node, i.e., non-6LBR node.   |           |
    +--------+-------+------------------------------+-----------+
    | 6LBR   | 1     | The pledge requests to play  | RFC 9031  |
    |        |       | the role of 6LoWPAN Border   |           |
    |        |       | Router (6LBR).               |           |
    +--------+-------+------------------------------+-----------+

                        Table 3: Role values.

8.4.2. Configuration Object โœจ

okay so the Configuration structure is built on a CBOR map object ๐ŸŽ€ and there's a whole lineup of parameters that can live inside it ๐Ÿ’…. You can look up the labels in the "Constrained Join Protocol (CoJP) Parameters" registry, which is Section 11.1, so bookmark that bestie ๐Ÿ”–

link-layer key set: this is an array full of cryptographic keys and their identifiers ๐Ÿ”‘ that are either being used in the network right now or are scheduled to be used later. Individual keys are encoded as described in Section 8.4.3. The link-layer key set parameter MAY be included in a Configuration object. When it's there, it MUST contain at least one key ๐Ÿซถ. This parameter is also how rekeying gets done in the network ๐Ÿ”„ The installation and use of keys is different for the 6LBR and for other (regular) nodes, and Sections 8.4.3.1 and 8.4.3.2 explain it all.

short identifier: a compact identifier assigned to the pledge ๐Ÿงš The structure is described in Section 8.4.4. The short identifier parameter MAY be included in a Configuration object ๐ŸŒธ

JRC address: this is the IPv6 address of the JRC, encoded as a byte string that's exactly 16 bytes long ๐ŸŒˆ If the byte string is any other length, the parameter MUST be discarded ๐Ÿ™ˆ If the JRC is NOT co-located with the 6LBR and has a different IPv6 address than the 6LBR, this parameter MUST be included. In the special case where the JRC is co-located with the 6LBR and has the same IPv6 address as the 6LBR, this parameter MAY be included. And if the JRC address parameter isn't in the Configuration object at all, that means the JRC has the same IPv6 address as the 6LBR ๐Ÿฆ‹ The joined node can then find the JRC's address through network control traffic. See Section 6.

blacklist: this is an array of pledge identifiers that the JRC has blacklisted ๐Ÿšซ, with each pledge identifier encoded as a byte string. The blacklist parameter MAY be included in a Configuration object. When it's present, the array MUST contain zero or more byte strings encoding pledge identifiers. The joined node MUST silently drop any link-layer frames that come from the pledge identifiers in the blacklist parameter ๐Ÿคซ When this parameter is received, its value MUST overwrite any previously set values, so the newest list wins ๐Ÿ‘‘ This is how the JRC lets the JP node filter out traffic from misconfigured or malicious pledges before it gets forwarded into the network ๐Ÿ›ก๏ธ And if the JRC wants to take a pledge identifier off the blacklist, it just leaves that identifier out of the blacklist parameter value it sends next.

Heads up: since the blacklist parameter carries pledge identifiers, privacy considerations apply ๐Ÿ‘€ Go read Section 10 before you do anything with it, seriously.

join rate: this is the average data rate (in bytes/second) of join traffic forwarded into the network, encoded as an unsigned integer ๐Ÿ“ˆ It's the limit that shouldn't be exceeded when a joined node is operating as a JP. The join rate parameter MAY be included in a Configuration object. It lets the JRC set up different nodes in the network as JPs and, if there's an attack, throttle how fast the JP forwards unauthenticated traffic into the network ๐Ÿšฆ When this parameter is present in a Configuration object, its value MUST be used to set the PROBING_RATE of CoAP at the joined node for communication with the JRC โš™๏ธ If this parameter is set to zero, a joined node MUST silently drop any join traffic coming from unauthenticated pledges ๐Ÿ›‘ If this parameter is omitted, the value of positive infinity SHOULD be assumed โ™พ๏ธ A node operating as a JP MAY use another mechanism, which is out of scope for this specification, to configure the PROBING_RATE of CoAP when there's no join rate parameter in the Configuration object.

Table 4 is the cheat sheet for the parameters that can show up in a Configuration object ๐Ÿ“‹

         +====================+=======+==================+
         | Name               | Label | CBOR Type        |
         +====================+=======+==================+
         | link-layer key set | 2     | array            |
         +--------------------+-------+------------------+
         | short identifier   | 3     | array            |
         +--------------------+-------+------------------+
         | JRC address        | 4     | byte string      |
         +--------------------+-------+------------------+
         | blacklist          | 6     | array            |
         +--------------------+-------+------------------+
         | join rate          | 7     | unsigned integer |
         +--------------------+-------+------------------+

           Table 4: Summary of Configuration parameters.

Here's the CDDL fragment that represents everything above for the Configuration ๐Ÿงฉ The structures Link_Layer_Key and Short_Identifier are specified in Sections 8.4.3 and 8.4.4, respectively.

Configuration = {
    ? 2 : [ +Link_Layer_Key ],   ; link-layer key set
    ? 3 : Short_Identifier,      ; short identifier
    ? 4 : bstr,                  ; JRC address
    ? 6 : [ *bstr ],             ; blacklist
    ? 7 : uint                   ; join rate
}

And Table 5 is the full map of CoJP parameter labels, including the ones that aren't in the Configuration list above, like role and network identifier ๐Ÿ—‚๏ธ

+===============+=======+==========+====================+===========+
| Name          | Label | CBOR     | Description        | Reference |
|               |       | type     |                    |           |
+===============+=======+==========+====================+===========+
| role          | 1     | unsigned | Identifies the     | RFC 9031  |
|               |       | integer  | role parameter     |           |
+---------------+-------+----------+--------------------+-----------+
| link-layer    | 2     | array    | Identifies the     | RFC 9031  |
| key set       |       |          | array carrying     |           |
|               |       |          | one or more        |           |
|               |       |          | link-layer         |           |
|               |       |          | cryptographic      |           |
|               |       |          | keys               |           |
+---------------+-------+----------+--------------------+-----------+
| short         | 3     | array    | Identifies the     | RFC 9031  |
| identifier    |       |          | assigned short     |           |
|               |       |          | identifier         |           |
+---------------+-------+----------+--------------------+-----------+
| JRC address   | 4     | byte     | Identifies the     | RFC 9031  |
|               |       | string   | IPv6 address       |           |
|               |       |          | of the JRC         |           |
+---------------+-------+----------+--------------------+-----------+
| network       | 5     | byte     | Identifies the     | RFC 9031  |
| identifier    |       | string   | network            |           |
|               |       |          | identifier         |           |
|               |       |          | parameter          |           |
+---------------+-------+----------+--------------------+-----------+
| blacklist     | 6     | array    | Identifies the     | RFC 9031  |
|               |       |          | blacklist          |           |
|               |       |          | parameter          |           |
+---------------+-------+----------+--------------------+-----------+
| join rate     | 7     | unsigned | Identifier the     | RFC 9031  |
|               |       | integer  | join rate          |           |
|               |       |          | parameter          |           |
+---------------+-------+----------+--------------------+-----------+
| unsupported   | 8     | array    | Identifies the     | RFC 9031  |
| configuration |       |          | unsupported        |           |
|               |       |          | configuration      |           |
|               |       |          | parameter          |           |
+---------------+-------+----------+--------------------+-----------+

                 Table 5: CoJP parameters map labels.

okay so bestie, the Link_Layer_Key structure is the whole bundle of settings the link-layer security module needs to get going ๐ŸŽ€. It holds the key identifier, the actual cryptographic key value, the link-layer algorithm identifier, the security level and frame types it should be used with for both outgoing and incoming security operations, plus any extra info needed to configure the key ๐Ÿ’…. The vibe is totally "everything you need, nothing you don't" ๐Ÿซถ.

For encoding compactness, the Link_Layer_Key object is NOT wrapped in a top-level CBOR object ๐Ÿ™ˆ. Instead it gets transported as a sequence of CBOR elements [RFC8742], and some of those elements are optional ๐Ÿงš.

Here are the parameters that can show up in a Link_Layer_Key object, in order ๐Ÿ’–:

key_id: The identifier of the key, encoded as a CBOR unsigned integer. This parameter MUST be included. If the decoded CBOR unsigned integer value is larger than the maximum link-layer key identifier, the key is considered invalid. If the key is considered invalid, the key MUST be discarded, and the implementation MUST signal the error as specified in Section 8.3.1. ๐Ÿšซ

key_usage: The identifier of the link-layer algorithm, security level, and link-layer frame types that can be used with the key, encoded as an integer. This parameter MAY be included. Possible values and the corresponding link-layer settings are specified in the IANA "Constrained Join Protocol (CoJP) Key Usage" registry (Section 11.2). If the parameter is omitted, the default value of 0 (6TiSCH-K1K2-ENC-MIC32) from Table 6 MUST be assumed. This default value has been chosen because it results in byte savings in the most constrained settings; its selection does not imply a recommendation for its general usage. ๐Ÿฆ‹

key_value: The value of the cryptographic key, encoded as a byte string. This parameter MUST be included. If the length of the byte string is different than the corresponding key length for a given algorithm specified by the key_usage parameter, the key MUST be discarded, and the implementation MUST signal the error as specified in Section 8.3.1. ๐Ÿ”ฅ

key_addinfo: Additional information needed to configure the link-layer key, encoded as a byte string. This parameter MAY be included. The processing of this parameter is dependent on the link-layer technology in use and a particular keying mode. ๐ŸŒธ

To be able to decode the keys that are present in the link-layer key set and to identify individual parameters of a single Link_Layer_Key object, the CBOR decoder needs to differentiate between elements based on the CBOR type. For example, a uint that follows a byte string signals to the decoder that a new Link_Layer_Key object is being processed. ๐Ÿ‘‘

The CDDL fragment for the Link_Layer_Key that represents the text above follows ๐Ÿ’…:

Link_Layer_Key = (
      key_id             : uint,
    ? key_usage          : int,
      key_value          : bstr,
    ? key_addinfo        : bstr,
)

Here's Table 6 ๐Ÿ“Š (the Key Usage values), kept exactly as the spec has it:

+======================+=====+======================+===============+
|Name                  |Value|Algorithm             |Description    |
+======================+=====+======================+===============+
|6TiSCH-K1K2-ENC-MIC32 |0    |IEEE802154-AES-CCM-128|Use MIC-32 for |
|                      |     |                      |EBs, ENC-MIC-32|
|                      |     |                      |for DATA and   |
|                      |     |                      |ACKNOWLEDGMENT.|
+----------------------+-----+----------------------+---------------+
|6TiSCH-K1K2-ENC-MIC64 |1    |IEEE802154-AES-CCM-128|Use MIC-64 for |
|                      |     |                      |EBs, ENC-MIC-64|
|                      |     |                      |for DATA and   |
|                      |     |                      |ACKNOWLEDGMENT.|
+----------------------+-----+----------------------+---------------+
|6TiSCH-K1K2-ENC-MIC128|2    |IEEE802154-AES-CCM-128|Use MIC-128 for|
|                      |     |                      |EBs, ENC-      |
|                      |     |                      |MIC-128 for    |
|                      |     |                      |DATA and       |
|                      |     |                      |ACKNOWLEDGMENT.|
+----------------------+-----+----------------------+---------------+
|6TiSCH-K1K2-MIC32     |3    |IEEE802154-AES-CCM-128|Use MIC-32 for |
|                      |     |                      |EBs, DATA and  |
|                      |     |                      |ACKNOWLEDGMENT.|
+----------------------+-----+----------------------+---------------+
|6TiSCH-K1K2-MIC64     |4    |IEEE802154-AES-CCM-128|Use MIC-64 for |
|                      |     |                      |EBs, DATA and  |
|                      |     |                      |ACKNOWLEDGMENT.|
+----------------------+-----+----------------------+---------------+
|6TiSCH-K1K2-MIC128    |5    |IEEE802154-AES-CCM-128|Use MIC-128 for|
|                      |     |                      |EBs, DATA and  |
|                      |     |                      |ACKNOWLEDGMENT.|
+----------------------+-----+----------------------+---------------+
|6TiSCH-K1-MIC32       |6    |IEEE802154-AES-CCM-128|Use MIC-32 for |
|                      |     |                      |EBs.           |
+----------------------+-----+----------------------+---------------+
|6TiSCH-K1-MIC64       |7    |IEEE802154-AES-CCM-128|Use MIC-64 for |
|                      |     |                      |EBs.           |
+----------------------+-----+----------------------+---------------+
|6TiSCH-K1-MIC128      |8    |IEEE802154-AES-CCM-128|Use MIC-128 for|
|                      |     |                      |EBs.           |
+----------------------+-----+----------------------+---------------+
|6TiSCH-K2-MIC32       |9    |IEEE802154-AES-CCM-128|Use MIC-32 for |
|                      |     |                      |DATA and       |
|                      |     |                      |ACKNOWLEDGMENT.|
+----------------------+-----+----------------------+---------------+
|6TiSCH-K2-MIC64       |10   |IEEE802154-AES-CCM-128|Use MIC-64 for |
|                      |     |                      |DATA and       |
|                      |     |                      |ACKNOWLEDGMENT.|
+----------------------+-----+----------------------+---------------+
|6TiSCH-K2-MIC128      |11   |IEEE802154-AES-CCM-128|Use MIC-128 for|
|                      |     |                      |DATA and       |
|                      |     |                      |ACKNOWLEDGMENT.|
+----------------------+-----+----------------------+---------------+
|6TiSCH-K2-ENC-MIC32   |12   |IEEE802154-AES-CCM-128|Use ENC-MIC-32 |
|                      |     |                      |for DATA and   |
|                      |     |                      |ACKNOWLEDGMENT.|
+----------------------+-----+----------------------+---------------+
|6TiSCH-K2-ENC-MIC64   |13   |IEEE802154-AES-CCM-128|Use ENC-MIC-64 |
|                      |     |                      |for DATA and   |
|                      |     |                      |ACKNOWLEDGMENT.|
+----------------------+-----+----------------------+---------------+
|6TiSCH-K2-ENC-MIC128  |14   |IEEE802154-AES-CCM-128|Use ENC-MIC-128|
|                      |     |                      |for DATA and   |
|                      |     |                      |ACKNOWLEDGMENT.|
+----------------------+-----+----------------------+---------------+

Table 6: Key Usage values. ๐Ÿ“

8.4.3.1. Rekeying of 6LBRs ๐ŸŒˆ

okay this one is a big deal ๐Ÿ˜ญ. When the 6LBR receives the Configuration object containing a link-layer key set, it MUST immediately install and start using the new keys for all outgoing traffic, and it MUST remove any old keys it installed from the previous key set after a delay of COJP_REKEYING_GUARD_TIME has passed. This mechanism is how the JRC forces the 6LBR to start sending traffic with the new key. โญ

The JRC is the one who decides, once it has determined that the new key has been made available to all (or some overwhelming majority) of nodes. Any node the JRC has not reached at that point is either nonfunctional or in extended sleep such that it will not be reached. ๐Ÿ’€ To get the key update, a node like that will need to go through the join process anew. ๐Ÿซถ

8.4.3.2. Rekeying of 6LNs ๐Ÿฆ‹

when a regular 6LN receives the Configuration object with a link-layer key set, it MUST install the new keys โœจ. The 6LN will use both the old and the new keys to decrypt and authenticate any incoming traffic that arrives, based upon the key identifier in the packet. It MUST continue to use the old keys for all outgoing traffic until it has detected that the network has switched to the new key set. ๐Ÿงš

The detection of the network switch is based upon the receipt of traffic secured with the new keys. Upon the reception and the successful security processing of a link-layer frame secured with a key from the new key set, a 6LN MUST then switch to sending all outgoing traffic using the keys from the new set. The 6LN MUST remove any keys it had installed from the previous key set after waiting COJP_REKEYING_GUARD_TIME since it started using the new key set. ๐Ÿ”ฅ

Sending traffic with the new keys signals to other downstream nodes to switch to their new key, causing a ripple of key updates around each 6LBR. ๐ŸŒŠ๐Ÿ’œ

8.4.3.3. Use in IEEE Std 802.15.4 ๐Ÿ”Œโœจ

Okay, bestie, this bit is for when Link_Layer_Key is used with [IEEE802.15.4] ๐Ÿ’œ. The big idea is that the keying mode is signaled by the parameter values in the object itself. The key_id value together with key_addinfo tells you which of the four Key ID modes of [IEEE802.15.4] is in play and how it works ๐Ÿ”‘.

  • Key ID Mode 0x00 (Implicit, pairwise): key_id MUST be set to 0. key_addinfo MUST be present, and it MUST be set to the link-layer address(es) of a single peer that the key should be used with ๐Ÿซถ. Depending on how the network is configured, key_addinfo may carry the peer's long link-layer address (the pledge identifier), its short link-layer address, or both joined together with the long address coming first. Which address types show up is decided by the length of the byte string ๐Ÿ“.
  • Key ID Mode 0x01 (Key Index): key_id MUST be set to a value different from 0. key_addinfo MUST NOT be present ๐Ÿ™…โ€โ™€๏ธ.
  • Key ID Mode 0x02 (4-byte Explicit Key Source): key_id MUST be set to a value different from 0. key_addinfo MUST be present, and it MUST be set to a byte string that is exactly 4 bytes long. In this mode, key_addinfo carries the Key Source parameter used to configure [IEEE802.15.4] ๐ŸŽ€.
  • Key ID Mode 0x03 (8-byte Explicit Key Source): key_id MUST be set to a value different from 0. key_addinfo MUST be present, and it MUST be set to a byte string that is exactly 8 bytes long. Again, key_addinfo carries the Key Source parameter used to configure [IEEE802.15.4] ๐ŸŒธ.

No matter the mode, the key_usage parameter decides how a particular key gets used for incoming and outgoing security policies. It's giving policy ๐Ÿ’….

For Key ID Modes 0x01 through 0x03, key_id sets the "secKeyIndex" parameter of [IEEE802.15.4]. That value gets signaled in every outgoing frame secured with the given key ๐Ÿฆ‹. The highest value key_id can have is 254. The value 255 is reserved in [IEEE802.15.4], so it's considered invalid ๐Ÿšซ.

Key ID Mode 0x00 (Implicit, pairwise) lets the JRC act as a trusted third party and hand out pairwise keys between nodes in the network ๐Ÿ‘‘. How the JRC finds out about the network topology is out of scope for this spec. It could happen through 6LBR-JRC signaling, for example ๐Ÿงš. Pairwise keys could also come from a key agreement protocol run directly between the peers, where authentication is based on the symmetric cryptographic material the JRC gave to both of them. That protocol is also out of scope for this spec ๐Ÿ™ˆ.

Implementations MUST use different link-layer keys when they use different authentication tag (MIC) lengths. The reason is that reusing the same key with different tag lengths might be unsafe. So, for example, the same key can't be used for both MIC-32 and MIC-64 levels ๐Ÿ˜ญ. See Annex B.4.3 of [IEEE802.15.4] for more information.

8.4.4. Short Identifier ๐Ÿงธ

The Short_Identifier object represents an identifier assigned to the pledge. It's encoded as a CBOR array object, and it contains these things, in order:

  • identifier: The short identifier assigned to the pledge, encoded as a byte string. This parameter MUST be included. The identifier MUST be unique across the whole set of identifiers assigned in a network managed by a JRC. If the identifier is invalid, the decoder MUST silently ignore the Short_Identifier object ๐Ÿคซ.
  • lease_time: How long the identifier stays valid, measured in hours from when the CBOR object is received, encoded as a CBOR unsigned integer. This parameter MAY be included. The node MUST stop using the assigned short identifier once the lease_time interval expires โณ. It's up to the JRC to renew the lease before the previous interval runs out. To renew, the JRC runs the parameter update exchange with the node and includes the Short_Identifier in the Configuration object, as described in Section 8.2 ๐Ÿ”„. If the lease expires, the node SHOULD start a new join exchange, as described in Section 8.1. If this parameter is left out, the value of positive infinity MUST be assumed, which means the identifier stays valid for as long as the node is in the network โ™พ๏ธ.

Here's the CDDL fragment that captures the text above ๐Ÿ“œ:

Short_Identifier = [
      identifier        : bstr,
    ? lease_time        : uint
]

8.4.4.1. Use in IEEE Std 802.15.4 ๐Ÿ›œ

When Short_Identifier is used with [IEEE802.15.4], these considerations apply, okay?

The identifier MUST be used to set the short address of the IEEE Std 802.15.4 module ๐Ÿ“ก. When operating in TSCH mode, the identifier MUST be unique across the whole set of identifiers assigned in multiple networks that share link-layer key(s). If the byte string for the identifier parameter isn't 2 bytes long, the identifier is considered invalid. The values 0xfffe and 0xffff are reserved by [IEEE802.15.4], so using them is considered invalid too ๐Ÿšซ.

The security properties that the [IEEE802.15.4] link layer offers in TSCH mode depend on the short identifier (the short address) being unique. The short address is one of the inputs used to build the nonce that protects link-layer frames ๐Ÿ”. If something gets misconfigured and the same short address is assigned twice under the same link-layer key, the loss of security properties is imminent ๐Ÿ’€. For this reason, it is not safe to let the pledge generate the short identifier locally, because that is likely to cost you the link-layer security properties.

The JRC MUST make sure that at any given time there are never two of the same short identifiers in use under the same link-layer key ๐Ÿšจ. If the lease_time parameter of a given Short_Identifier object is set to positive infinity, take care that the identifier isn't handed to another node until the JRC is certain it's no longer in use. That might mean using out-of-band signaling. If the lease_time parameter expires for any reason, the JRC should take into consideration any ongoing transmissions from the joined node that might still be sitting in queues, before it assigns the same identifier to another node ๐Ÿซฃ.

Care is also needed when the pledge (joined node) sets up the lease expiration. The units of lease_time are hours counted from when the CBOR object is received, so the pledge needs to convert that time into the matching Absolute Slot Number in the network โฐ. The joined node (pledge) MUST only use the Absolute Slot Number as its time reference when it decides whether the assigned short identifier is still valid ๐Ÿง .

8.4.5. Unsupported Configuration Object ๐Ÿ™‹โ€โ™€๏ธ

The Unsupported_Configuration object is encoded as a CBOR array, and it has at least one Unsupported_Parameter object inside. Each Unsupported_Parameter object is a sequence of CBOR elements with no enclosing top-level CBOR object, which keeps things compact ๐Ÿ“ฆ. These are the parameters that can show up in an Unsupported_Parameter object, in order:

  • code: Says what the capability for acting on the parameter signaled by parameter_label is, encoded as an integer. This parameter MUST be included. The possible values are specified in the IANA "Constrained Join Protocol (CoJP) Unsupported Configuration Codes" registry (Section 11.3) ๐Ÿท๏ธ.
  • parameter_label: Identifies the parameter. This parameter MUST be included. The possible values are specified in the label column of the IANA "Constrained Join Protocol (CoJP) Parameters" registry" (Section 11.1) ๐Ÿ”–.
  • parameter_addinfo: Extra info about the parameter that can't be acted upon. This parameter MUST be included. If code is set to "Unsupported", parameter_addinfo gives extra information to the JRC ๐Ÿ’ฌ. If the parameter named by parameter_label can't be acted upon no matter what its value is, parameter_addinfo MUST be set to null. That tells the JRC it SHOULD NOT try to configure the parameter again ๐Ÿ›‘. If the pledge can act on the parameter but can't apply the setting that the parameter value indicates, the pledge can hint this to the JRC. In that case, parameter_addinfo MUST be set to the value of the parameter that can't be acted upon, following the normative parameter structure specified in this document ๐ŸŽฏ. For example, the link-layer key set object can be included to signal that either a subset or the whole key set that was received can't be acted upon. In that case, the value of parameter_addinfo follows the link-layer key set structure defined in Section 8.4.2 ๐Ÿ—๏ธ. If code is set to "Malformed", parameter_addinfo MUST be set to null, which again tells the JRC it SHOULD NOT try to configure the parameter again ๐Ÿ˜ต.

Here's the CDDL fragment for the Unsupported_Configuration and Unsupported_Parameter objects that represents the text above ๐Ÿ“:

Unsupported_Configuration = [
       + parameter           : Unsupported_Parameter
]

Unsupported_Parameter = (
         code                : int,
         parameter_label     : int,
         parameter_addinfo   : nil / any
)

And here are the values for the code field. Table 7 is the one to check:

 +=============+=======+==============================+===========+
 | Name        | Value | Description                  | Reference |
 +=============+=======+==============================+===========+
 | Unsupported | 0     | The indicated setting is not | RFC 9031  |
 |             |       | supported by the networking  |           |
 |             |       | stack implementation.        |           |
 +-------------+-------+------------------------------+-----------+
 | Malformed   | 1     | The indicated parameter      | RFC 9031  |
 |             |       | value is malformed.          |           |
 +-------------+-------+------------------------------+-----------+

          Table 7: Unsupported Configuration code values.

This section gives RECOMMENDED values of CoJP settings ๐ŸŒˆ. Here they are:

            +==========================+===============+
            | Name                     | Default Value |
            +==========================+===============+
            | COJP_MAX_JOIN_ATTEMPTS   | 4             |
            +--------------------------+---------------+
            | COJP_REKEYING_GUARD_TIME | 12 seconds    |
            +--------------------------+---------------+

                Table 8: Recommended CoJP settings.

The COJP_REKEYING_GUARD_TIME value SHOULD take into account possible retransmissions at the link layer, because wireless links aren't always perfect ๐Ÿ“ถ๐Ÿ’–

9. Security Considerations ๐Ÿ”

Okay bestie, this is the serious part ๐Ÿ˜ญ Since this document uses the pledge identifier to set the ID Context parameter of OSCORE, an important security requirement is that the pledge identifier is unique across all the pledge identifiers a JRC manages ๐Ÿ”‘. That uniqueness keeps the (key, nonce) pairs unique for the AEAD algorithm OSCORE uses ๐Ÿ™ˆ, and it lets the JRC grab the right security context when a Join Request shows up ๐ŸŽฏ. Managing identifiers gets easier if you use the globally unique EUI-64, but ngl that comes with privacy risks, which we'll get into in Section 10 ๐Ÿ’œ.

This doc also makes it a hard rule that the (6LBR) pledge and the JRC get unique PSKs ๐Ÿ—๏ธ. Provisioning a PSK to every pledge can be a real operational overhead ๐Ÿ˜ฎโ€๐Ÿ’จ, but it's vital for the security of the network ๐Ÿ’…. The PSK sets the OSCORE Master Secret during security context derivation, and that derivation produces OSCORE keys that matter for mutual authentication of the (6LBR) pledge and the JRC ๐Ÿค. The resulting security context shared between the pledge (joined node) and the JRC is used for joining, and it's long-lived: it can be used for the whole life of a joined node for parameter update exchanges โณ. If an attacker learns the PSK, a man-in-the-middle attack becomes possible ๐Ÿ’€.

Here's a tea moment โ˜•: OSCORE gives replay protection, but it doesn't give an indication of freshness when an attacker can drop and/or reorder traffic ๐Ÿ™ƒ. The Join Request has no randomness and the sequence number is predictable, so the JRC could in principle anticipate a Join Request from a particular pledge and pre-calculate the response ๐Ÿ”ฎ. In that scenario, the JRC doesn't have to be alive when the request arrives ๐Ÿ‘ป. This could matter if the JRC was temporarily compromised and the legitimate owner later got control back ๐Ÿ˜ค.

Generating and provisioning PSKs unsafely is a huge no-no ๐Ÿšซ. Sharing a single PSK across a group of devices is a classic pitfall that leads to poor security ๐Ÿ™…โ€โ™€๏ธ. Compromising one device is likely to compromise the entire batch, and then the attacker could impersonate a legit device, join the network, generate bogus data, and mess with network operation ๐Ÿ˜ฑ. Some vendors also scramble or hash device serial numbers or EUI-64 identifiers to make "unique" PSKs, but without any secret involved, the effort to break those methods is pretty low ๐Ÿฅด. That means an attacker could impersonate any device in the batch without even compromising one ๐Ÿ’€. Using cryptographically secure random number generators to generate the PSK is RECOMMENDED ๐ŸŽฒ, and see [NIST800-90A] for different mechanisms using deterministic methods ๐Ÿ“š.

The JP forwards the unauthenticated join traffic into the network ๐Ÿ“ก. A data cap on the JP stops it from forwarding more traffic than the network can handle, and it enables throttling during an attack ๐Ÿšง. This traffic can only be aimed at the JRC, so the JRC needs to be ready to handle unsanitized inputs ๐Ÿงฑ. The data cap can be configured by the JRC by including a join rate parameter in the Join Response, and it's implemented through the CoAP's PROBING_RATE setting ๐ŸŽ›๏ธ. A data cap at a JP forces attackers to use more than one JP if they want to overwhelm the network ๐ŸฅŠ. Marking join traffic packets with a nonzero DSCP lets the network carry the traffic if it has capacity, but it encourages the network to drop the extra traffic instead of adding bandwidth because of it ๐Ÿ“ฆ.

The shared nature of the "minimal" cell used for join traffic makes the network prone to DoS by congesting the JP with bogus traffic ๐ŸŒŠ. An attacker like this is limited by its maximum transmit power โšก. Having redundant JPs alleviates the issue and gives the pledge the chance to use the best available link for joining ๐Ÿ†. How a network node decides to become a JP is out of scope for this specification ๐Ÿคทโ€โ™€๏ธ.

At the beginning of the join process, the pledge has no way to verify the content of the EB, so it has to accept it at "face value" ๐Ÿซฃ. If the pledge tries to join an attacker's network, the Join Response will either fail the security check or time out โฐ. The pledge may implement a temporary blacklist to filter out undesired EBs and try the next seemingly valid EB ๐Ÿ“. This blacklist helps, but it's effectively limited by the node's available memory ๐Ÿง . Note that this temporary blacklist is different from the one communicated as part of the CoJP Configuration object, since it helps the pledge fight a DoS attack ๐Ÿ›ก๏ธ. Bogus beacons prolong the pledge's join time, and so does the time spent in "minimal" duty cycle mode [RFC8180] ๐Ÿข. The blacklist communicated as part of the CoJP Configuration object helps the JP fight a DoS attack by a malicious pledge ๐Ÿฅท.

During the network lifetime, the JRC may at any time start a parameter update exchange with a joined node ๐Ÿ“ฃ. The Parameter Update message uses the same OSCORE security context as the join exchange, except the server and client roles are swapped ๐Ÿ”„. So every Parameter Update message carries the well-known OSCORE Sender ID of the JRC ๐Ÿชช. A passive attacker can use that Sender ID to identify Parameter Update traffic if the link-layer protection doesn't provide confidentiality ๐Ÿ‘€. A countermeasure against this traffic-analysis attack is to use encryption at the link layer ๐Ÿ”’. Note that join traffic doesn't get link-layer protection at the first hop, since the pledge isn't yet in possession of cryptographic keys ๐Ÿ™…โ€โ™€๏ธ. EB traffic in the network isn't encrypted either, which makes it super easy for a passive attacker to spot these types of traffic ๐Ÿ‘๏ธ.


10. Privacy Considerations ๐Ÿ•ต๏ธโ€โ™€๏ธ

The join solution in this document relies on the uniqueness of the pledge identifier across all pledge identifiers managed by a JRC ๐Ÿ”. This identifier is transferred in the clear as an OSCORE 'kid context' ๐Ÿ“ญ. Using the globally unique EUI-64 as the pledge identifier simplifies management but comes with certain privacy risks ๐Ÿ˜ฌ. The implications are thoroughly discussed in [RFC7721], and they include correlation of activities over time, location tracking, address scanning, and device-specific vulnerability exploitation ๐Ÿง. Since the join process happens rarely compared to the network lifetime, the long-term threats from using EUI-64 as the pledge identifier are minimal ๐Ÿ™Œ. After the join process completes, though, using EUI-64 in the form of a Layer 2 or Layer 3 address stretches those privacy threats into the long term ๐Ÿ˜ฉ.

As an optional mitigation technique ๐Ÿ’ซ, the Join Response message may contain a short address assigned by the JRC to the (6LBR) pledge ๐Ÿฆ‹. The assigned short address SHOULD be uncorrelated with the long-term pledge identifier ๐Ÿ™…. The short address is encrypted in the response ๐Ÿ”. Once the join process completes, the new node may use the short addresses for all further Layer 2 (and Layer 3) operations ๐ŸŒธ. This reduces the privacy threats, because the short Layer 2 address (visible even when the network is encrypted) doesn't disclose the manufacturer the way EUI-64 does ๐Ÿ’–. However, an eavesdropper with access to the radio medium during the join process may be able to correlate the assigned short address with the extended address based on timing information with a non-negligible probability ๐Ÿ˜ณ. That probability decreases as the number of pledges joining concurrently increases ๐Ÿ“‰.


11. IANA Considerations ๐Ÿ“‹

This document allocates a well-known name under the .arpa name space according to the rules given in [RFC3172] and [RFC6761] ๐ŸŒ. The name 6tisch.arpa is requested ๐ŸŽ€. No subdomains are expected, and adding any such subdomains requires the publication of an IETF Standards Track RFC ๐Ÿ™…โ€โ™€๏ธ. No A, AAAA, or PTR record is requested.

11.1. Constrained Join Protocol (CoJP) Parameters

This section defines a subregistry within the "IPv6 Over the TSCH Mode of IEEE 802.15.4 (6TiSCH)" registry with the name "Constrained Join Protocol (CoJP) Parameters" ๐Ÿงฉ.

The columns of the registry are:

  • Name: A descriptive name that makes the item easier to reference ๐Ÿท๏ธ. It isn't used in the encoding. The name MUST be unique.
  • Label: The value used to identify this parameter. The label is an integer. The label MUST be unique.
  • CBOR Type: This field contains the CBOR type for the field.
  • Description: This field contains a brief description for the field. The description MUST be unique.
  • Reference: This field contains a pointer to the public specification for the field, if one exists.

This registry is populated with the values in Table 5 ๐Ÿ“Š.

The amending formula for this subregistry is: Different ranges of values use different registration policies [RFC8126]. Here's how the ranges break down ๐ŸŒˆ:

  • Integer values from -256 to 255 are designated as Standards Action.
  • Integer values from -65536 to -257 and from 256 to 65535 are designated as Specification Required.
  • Integer values greater than 65535 are designated as Expert Review.
  • Integer values less than -65536 are marked as Private Use.

11.2. Constrained Join Protocol (CoJP) Key Usage

This section defines a subregistry within the "IPv6 Over the TSCH Mode of IEEE 802.15.4 (6TiSCH)" registry with the name "Constrained Join Protocol (CoJP) Key Usage" ๐Ÿ”.

The columns of this registry are:

  • Name: A descriptive name that enables easier reference to the item. It isn't used in the encoding. The name MUST be unique.
  • Value: The value used to identify the key usage setting. These values MUST be unique. The value is an integer.
  • Algorithm: A descriptive name of the link-layer algorithm in use, which uniquely determines the key length. The name isn't used in the encoding. The algorithm MUST be unique.
  • Description: A description of the key usage setting. The field should describe in enough detail how the key is to be used with different frame types, specific for the link-layer technology in question. The description MUST be unique.
  • Reference: This contains a pointer to the public specification for the field, if one exists.

This registry is populated with the values in Table 6 ๐ŸŽฏ.

The amending formula for this subregistry is: Different ranges of values use different registration policies [RFC8126]. Here's how the ranges break down ๐ŸŒˆ:

  • Integer values from -256 to 255 are designated as Standards Action.
  • Integer values from -65536 to -257 and from 256 to 65535 are designated as Specification Required.
  • Integer values greater than 65535 are designated as Expert Review.
  • Integer values less than -65536 are marked as Private Use.

11.3. Constrained Join Protocol (CoJP) Unsupported Configuration Codes ๐ŸŽ€

okay so this section sets up a little subregistry inside the "IPv6 Over the TSCH Mode of IEEE 802.15.4 (6TiSCH)" registry, and it's called "Constrained Join Protocol (CoJP) Unsupported Configuration Codes" ๐Ÿ“‹ very organised, very giving librarian energy ๐Ÿ’œ

Every entry in this registry has four columns:

  • Name: a descriptive name so people can refer to the item more easily. It's not used in the encoding. The name MUST be unique. ๐Ÿ’…
  • Value: the value that identifies the diagnostic code. These values MUST be unique. The value is an integer. ๐Ÿ”ข
  • Description: a descriptive human-readable name. The description MUST be unique. It is not used in the encoding. ๐Ÿ“
  • Reference: a pointer to the public specification for the field, if one exists. ๐Ÿ”—

This registry is going to be populated with the values in Table 7 ๐ŸŒธ

Now for the rules about adding new stuff (the amending formula, aka the registration policy bit ๐Ÿ‘‘). Different ranges of values use different registration policies [RFC8126], so it's not one-size-fits-all bestie:

  • Integer values from -256 to 255 are designated as Standards Action.
  • Integer values from -65536 to -257 and from 256 to 65535 are designated as Specification Required.
  • Integer values greater than 65535 are designated as Expert Review.
  • Integer values less than -65536 are marked as Private Use.

no cap this is a lot of number ranges but the pattern is clean โœจ


12. References ๐Ÿ“š

12.1. Normative References

12.2. Informative References

Appendix A. Example ๐Ÿงš

Okay bestie, Figure 3 shows a successful join protocol exchange, and it's so satisfying to watch ๐Ÿฆ‹ The pledge sets up its OSCORE context and derives the OSCORE keys and nonces from the PSK. Then it uses that context to protect the Join Request. The request has a Proxy-Scheme option, the well-known host name of the JRC in the Uri-Host option, and the pledge's EUI-64 as both its pledge identifier and its OSCORE 'kid context' ๐Ÿ’…

The Proxy-Scheme option is what kicks things off. When the JP sees it, the JP forwards the request to the JRC and sets the CoAP token to the internal state it needs. The JP already knows the IPv6 address of the JRC, because it learned it back when it was a pledge and joined the network ๐ŸŒธ Once the JRC gets the request, it finds the right context by looking at the 'kid context' parameter. The OSCORE data authenticity verification makes sure nobody messed with the request in transit, and replay protection comes from persistently handling the mutable context parameters. Iconic, honestly ๐Ÿ‘‘

When the JP gets the Join Response, it checks the state inside the CoAP token before it decides where to forward. Then it sets its internal state to whatever the token says and sends the Join Response to the correct pledge. Quick note: the JP can't decrypt the CoJP object (the configuration) in the payload, and it doesn't need to. Back at the pledge, the Join Response gets matched to the Join Request and checked for replay protection using the OSCORE processing rules ๐Ÿ” In this example, the Join Response doesn't include the IPv6 address of the JRC, so the pledge figures out that the JRC is co-located with the 6LBR. Main character energy ๐Ÿ’œ

      <-----E2E OSCORE------>
    Client      Proxy     Server
    Pledge       JP        JRC
      |          |          |
      |  Join    |          |            Code: 0.02 (POST)
      | Request  |          |           Token: -
      +--------->|          |    Proxy-Scheme: coap
      |          |          |        Uri-Host: 6tisch.arpa
      |          |          |          OSCORE: kid: -,
      |          |          |                  kid_context: EUI-64,
      |          |          |                  Partial IV: 1
      |          |          |         Payload: { Code: 0.02 (POST),
      |          |          |                    Uri-Path: "j",
      |          |          |                    join_request, <Tag> }
      |          |          |
      |          |  Join    |            Code: 0.02 (POST)
      |          | Request  |           Token: opaque state
      |          +--------->|          OSCORE: kid: -,
      |          |          |                  kid_context: EUI-64,
      |          |          |                  Partial IV: 1
      |          |          |         Payload: { Code: 0.02 (POST),
      |          |          |                    Uri-Path: "j",
      |          |          |                    join_request, <Tag> }
      |          |          |
      |          |          |
      |          |  Join    |            Code: 2.04 (Changed)
      |          | Response |           Token: opaque state
      |          |<---------+          OSCORE: -
      |          |          |         Payload: { Code: 2.04 (Changed),
      |          |          |                    configuration, <Tag> }
      |          |          |
      |          |          |
      |  Join    |          |            Code: 2.04 (Changed)
      | Response |          |           Token: -
      |<---------+          |          OSCORE: -
      |          |          |         Payload: { Code: 2.04 (Changed),
      |          |          |                    configuration, <Tag> }
      |          |          |

     Figure 3: Example of a successful join protocol exchange. { ... }
            denotes authenticated encryption, <Tag> denotes the
                            authentication tag.

Here's the join_request object we're working with ๐Ÿ“

join_request:
{
   5 : h'cafe' / PAN ID of the network pledge is attempting to join /
}

Since the role parameter isn't present, the default role of "6TiSCH Node" is implied. โœจ

The join_request object gets converted to h'a10542cafe', which is 5 bytes. Bestie, that's so compact ๐Ÿฅน

And here's the configuration object:

configuration:
{
   2 : [           / link-layer key set /
         1,        / key_id /
         h'e6bf4287c2d7618d6a9687445ffd33e6' / key_value /
       ],
   3 : [           / short identifier /
         h'af93'   / assigned short address /
       ]
}

Since the key_usage parameter isn't present in the link-layer key set object, the default value of "6TiSCH-K1K2-ENC-MIC32" is implied. Since the key_addinfo parameter isn't present and key_id is different from 0, Key ID Mode 0x01 (Key Index) is implied. ๐Ÿ”‘ Similarly, since the lease_time parameter isn't present in the short identifier object, the default value of positive infinity is implied. Forever vibes โ™พ๏ธ

The configuration object gets converted to h'a202820150e6bf4287c2d7618d6a9687445ffd33e6038142af93', which is 26 bytes. Slay ๐Ÿ’–


Appendix B. Lightweight Implementation Option ๐ŸŒˆ

Okay so, sometimes you're working in an environment where squeezing the implementation footprint matters a lot ๐Ÿฅบ In that case, you can implement this specification without putting HKDF [RFC5869] and SHA [RFC4231] on your constrained devices. Both of them are used during the OSCORE security context derivation phase, and that derivation can also be done by the JRC or a provisioning device on behalf of the (6LBR) pledge during the provisioning phase. When that happens, the derived OSCORE security context parameters get written straight into the (6LBR) pledge, so the PSK never has to be provisioned to the (6LBR) pledge at all ๐Ÿฆ‹

Here's why the HKDF part matters. Using HKDF to derive the OSCORE security context parameters means the resulting OSCORE keys have good security properties, and they stay unique as long as the input varies between pledges. This specification keeps that uniqueness by requiring unique pledge identifiers and a unique PSK for each (6LBR) pledge. From the AEAD nonce reuse viewpoint, having a unique pledge identifier is a sufficient condition. ๐Ÿ”

But, and this is a big but ๐Ÿ™ˆ, Section 9 talks about how using one PSK shared across many devices is a common security pitfall. If that shared PSK gets compromised on a single device, the entire batch is compromised too. With the implementation and deployment scheme described above, the PSK doesn't need to be written to individual pledges. So even if a shared PSK is used, the scheme gives you a level of security comparable to the scenario where each pledge has its own unique PSK. Still, there's a latent risk: the shared PSK could be compromised on the provisioning device, and that would compromise all the devices in the batch. Be careful with that one, besties ๐Ÿ˜ญ๐Ÿซถ


Acknowledgments ๐Ÿซถ

The work on this document has been partially supported by the European Union's H2020 Programme for research, technological development and demonstration under grant agreements: No. 644852, project ARMOUR; No. 687884, project F-Interop and open-call project SPOTS; No. 732638, project Fed4FIRE+ and open-call project SODA. ๐Ÿ‡ช๐Ÿ‡บโœจ

The following individuals provided input to this document (in alphabetic order): Christian Amsรผss, Tengfei Chang, Roman Danyliw, Linda Dunbar, Vijay Gurbani, Klaus Hartke, Barry Leiba, Benjamin Kaduk, Tero Kivinen, Mirja Kรผhlewind, John Mattsson, Hilarie Orman, Alvaro Retana, Adam Roach, Jim Schaad, Gรถran Selander, Yasuyuki Tanaka, Pascal Thubert, William Vignat, Xavier Vilajosana, ร‰ric Vyncke, and Thomas Watteyne. Thank you all, you're literally the best ๐Ÿ’œ๐ŸŒธ


Authors' Addresses ๐Ÿ’Œ

Maliลกa Vuฤiniฤ‡ (editor) Inria 2 Rue Simone Iff 75012 Paris France

Email: malisa.vucinic@inria.fr

Jonathan Simon Analog Devices 32990 Alvarado-Niles Road, Suite 910 Union City, CA 94587 United States of America

Email: jonathan.simon@analog.com

Kris Pister University of California Berkeley 512 Cory Hall Berkeley, CA 94720 United States of America

Email: pister@eecs.berkeley.edu

Michael Richardson Sandelman Software Works 470 Dawson Avenue Ottawa ON K1Z5V7 Canada

Email: mcr+ietf@sandelman.ca

๐Ÿ“ฆ where this page comes from