Concerns about the CoJP message size, the need for BLOCK-WISE options and the use of NON-confirmable messages

Issue #19 resolved
JRTI created an issue

Hello,

First, I wanted to thank you for all the efforts you put into this draft, its simplicty and reliance on already existing technologies truly are among its greatest assets in my opinion.

After finishing the test implementation of the latest OSCORE draft, I am starting to do the same with this draft, and while testing the serialization of messages, I noticed the large message size, which to be honest I hadn't expected while just reading this draft.

You will find bellow a detailed estimation of both the available CoAP message size using secured 802.15.4 messages and the CoJP size (at the CoAP level).

Without getting into the details so far, the conclusion, provided my predictions and calculations are good enough, is that although the CoJP request is surprisingly large, it manages to (barely sometimes, depending on the configuration used) be held into one unique 802.15.4 message. However the CoJP response will almost always overflow from such a message, and a way to fragment this message would be needed.

Now the BLOCK-WISE option is being devised just for this, however it is not really meant to be used with non-confirmable messages for obvious reasons such as packet loss...

I understand the reasonning behind making the request a NON-confirmable message to reduce the strain on the JP and the potential DoS, however maybe the answer(i mean the CoJP response) should at least be a CONfirmable message so that it can easilly be fragmented using BLOCK-WISE ?

Since none of this is mentionned in the draft, I would like to know if a thought has been given to this particular problem, and if so if a solution that I'm not seeing has been chosen ?

Thank you in advance,

Sincerely


Rough payload size assessment:

The maximum 802.15.4 message size is 127B.

Lets take the example of a CoJP message being relayed on the network (during one of multiple hops), thus being protected both by OSCORE at the CoAP level, and by the L2 security that secures all the exchanges inside the network.

Typical L2 (802.15.4) overhead with:

  • an 8-byte(medium security) MIC
  • a 6-byte aux security header by relying on the fact that both addresses are present in the frame and thus KeyIdMode 0x00 or 0x01 can be used
  • 8-byte MAC addresses since 2-byte short addresses at the L2 level are currently not supported in a lot of implementations, among which OpenWSN, but we don't use 10-byte addresses because we assume no PAN-id

In the end:

Frame control: 2 | Sequence nb: 1 | DstMAC@: 8 | SrcMAC@: 8 | Aux security header: 6 |  ...PAYLOAD...  | MIC: 8 | CRC: 2

TOTAL: 35B

Typical IPv6 overhead with:

  • a 2-byte compressed header (not counting the addresses)
  • 8-byte IPv6 addresses (max would be 16 each, best case could optimize further by distributing addresses so that the common part can be ommited), but in this case, let's assume we don't specify the network, and we just specify the 8 bytes that correspond to the MAC address.

In the end:

LOWPAN_IPHC: 2 | Src IP @: 8 | Dst IP @: 8 ||

TOTAL: 18B

UDP overhead: 8B

Total message overhead: 61B

Thus leaving: 66B


Now let's look at the size of the CoAP requests and response we actually want to send:

For all the OSCORE processing, if you want to verify the content of the messages by decrypting them, the following inputs have been used:

  • Master secret:{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}
  • Master salt: <empty>
  • Client(pledge) sid: {0x00}
  • Server(JRC) sid: {0x4a, 0x52,0x43}
  • The chosen 8-byte pledge identifier serving as kid/id-context was {0xaa,0xbb,0xcc,0xdd, 0xee,0xff,0x0a,0x0b}

/!\ NOTE that the newest version of the OSCORE draft, available here was used, this version changes the processing of the input into the derived parameters and it adds the kid-context to this processing, thus changing completely the derived parameters and if you use an outdated implementation, you will not get those same results.

The following were thus derived and used:

  • Common iv (size 13): { 0x30 0xD9 0x4B 0x12 0x25 0xA2 0x7A 0x24 0x83 0xE6 0x20 0x11 0xEF }
  • Client sender key (size 16): { 0x8F 0x8B 0xC9 0x1B 0x3C 0xD0 0xAB 0xE4 0x19 0xB7 0xCF 0x22 0xF5 0x75 0xDA 0x12 }
  • Client recipient key (size 16): { 0x26 0x3C 0x8A 0x53 0x03 0xBE 0xF8 0x49 0x95 0x9E 0x95 0x9F 0xF3 0x42 0x58 0x4E }

Note also that the first piv used in the request is 0x14 and not 0x00 for testing purposes.

For the CoAP processing:

  • Message ID: {0xBA,0xAD}
  • CoAP token: {0xDE, 0xAD, 0xBA, 0xBE}

For the CoJP request, a network id of size 4, e.g. "net1" was chosen, and the role is 0, i.e. node.

The cbor payload in diagnostic notation is:

{
    5: h'6E657431'
}

The obtained CoAP request looks as such (just leaving the pledge, before the JP): -----JoinRequest for Node-----

CoAP-Message:{
    version=1 , type=1 , TKL=4, Code=2, messageID=47789 ,token (size 4):    0xDE 0xAD 0xBA 0xBE

    options=    
        CoAP-Opt: type=3 , value (size 11): 0x36 0x74 0x69 0x73 0x63 0x68 0x2E 0x61 0x72 0x70 0x61

        CoAP-Opt: type=39 , value (size 4): 0x63 0x6F 0x61 0x70

        CoAP-Opt: type=11 , value (size 1): 0x6A

    payload (size 7):   0xA1 0x05 0x44 0x6E 0x65 0x74 0x31 
}

serialized coap (size 36):

0x54 0x02 0xBA 0xAD 0xDE 0xAD 0xBA 0xBE 0x3B 0x36 0x74 0x69 0x73 0x63 0x68 0x2E 0x61 0x72 0x70 0x61 0x81 0x6A 0xD4 0x0F 0x63 0x6F 0x61 0x70 0xFF 0xA1 0x05 0x44 0x6E 0x65 0x74 0x31

And once it has been secured by OSCORE (the OSCORE option + inner/outer code + potential payload marker bytes (0xFF) added to it... +8 bytes of MAC), it looks like this:

OSCORE-Message:{
    version=1 , type=1 , TKL=4, Code=2, messageID=47789 ,token (size 4):    0xDE 0xAD 0xBA 0xBE

    options=    
        CoAP-Opt: type=3 , value (size 11): 0x36 0x74 0x69 0x73 0x63 0x68 0x2E 0x61 0x72 0x70 0x61

        CoAP-Opt: type=21 , value (size 12):    0x19 0x15 0x08 0xAA 0xBB 0xCC 0xDD 0xEE 0xFF 0x0A 0x0B 0x00

    payload (size 25):  0x5E 0x6C 0x59 0x4C 0xE8 0x11 0xBB 0x23 0x7D 0xD1 0xC8 0x9A 0x07 0x79 0xD7 0x96 0x2F 0x46 0x83 0x43 0x41 0x35 0xF8 0x4B 0xEA 
}

serialized oscore (size 60):

0x54 0x02 0xBA 0xAD 0xDE 0xAD 0xBA 0xBE 0x3B 0x36 0x74 0x69 0x73 0x63 0x68 0x2E 0x61 0x72 0x70 0x61 0xDC 0x05 0x19 0x15 0x08 0xAA 0xBB 0xCC 0xDD 0xEE 0xFF 0x0A 0x0B 0x00 0xFF 0x5E 0x6C 0x59 0x4C 0xE8 0x11 0xBB 0x23 0x7D 0xD1 0xC8 0x9A 0x07 0x79 0xD7 0x96 0x2F 0x46 0x83 0x43 0x41 0x35 0xF8 0x4B 0xEA

This is already a sizeable message, even though it doesn't really contain any data, we have to remember that in our previous estimation of 66 available bytes, we counted the securing of the 802.15.4 frames (of size 6+8=14 bytes), which is not present for this message since it is exchanged on the unsecured part of the network between the pledge and the JP.

Therefore we are still ~ 20 bytes away from the actual limit here, but if 16-byte IPv6 addresses (instead of 8-byte) had been used this message would be on the brink of needing fragmentation.

The translation in the JP removes the "6tisch.arpa" (size 11+1) option, it thus reduces the size of the message by 12 bytes. However it also needs to add the stateless proxy option: The stateless proxy option value is unspecified (as it should be), but it MUST contain at least a timestamp/sequence number, so let's say a 4-byte integer, and it should logically at least contain the MAC address of the joining node, so an 8-byte address. In the end we end up with a ~13-byte (12+1 for the option 'header') stateless proxy option. Thus after translation in the JP, the message is of size 60-11+13= 62 bytes, and this is dangerously close to the limit we saw before.

-----JoinRequest transfer by JP-----

CoAP-Message:{
    version=1 , type=1 , TKL=4, Code=2, messageID=47789 ,token (size 4):    0xDE 0xAD 0xBA 0xBE

    options=    
        CoAP-Opt: type=21 , value (size 12):    0x19 0x15 0x08 0xAA 0xBB 0xCC 0xDD 0xEE 0xFF 0x0A 0x0B 0x00

        CoAP-Opt: type=55 , value (size 12):    0x61 0x62 0x62 0x61 0x2E 0x69 0x73 0x2E 0x62 0x61 0x63 0x6B

    payload (size 25):  0x5E 0x6C 0x59 0x4C 0xE8 0x11 0xBB 0x23 0x7D 0xD1 0xC8 0x9A 0x07 0x79 0xD7 0x96 0x2F 0x46 0x83 0x43 0x41 0x35 0xF8 0x4B 0xEA 
}

serialized coap (size 62):

0x54 0x02 0xBA 0xAD 0xDE 0xAD 0xBA 0xBE 0xDC 0x08 0x19 0x15 0x08 0xAA 0xBB 0xCC 0xDD 0xEE 0xFF 0x0A 0x0B 0x00 0xDC 0x15 0x61 0x62 0x62 0x61 0x2E 0x69 0x73 0x2E 0x62 0x61 0x63 0x6B 0xFF 0x5E 0x6C 0x59 0x4C 0xE8 0x11 0xBB 0x23 0x7D 0xD1 0xC8 0x9A 0x07 0x79 0xD7 0x96 0x2F 0x46 0x83 0x43 0x41 0x35 0xF8 0x4B 0xEA

Now the CoJP response is "obviously" going to be bigger, since it actually contains a lot of information, among which at least one 16-byte key... I chose to take a message that would also contain a 16-byte JRC address of "qwertyuioplkjhgf" (no trailing \0), and an 8-byte assigned pledge address of {0xa1,0xb1,0xc1,0xd1,0xe1,0xf1,0xa2,0xb2} available for one year, i.e. 31450000 seconds.

We can note here that the requirement for the JRC address to be exactly 16 bytes and not either 2,8 or 16 bytes long seems unreasonnable, at least 8 bytes could be properly shaved off of this in our example.

The cbor payload in diagnostic notation is:

{
    2: [1, 2, h'112233445566778899AABBCCDDEEFF11'], 
    3: [h'A1B1C1D1E1F1A2B2', 31450000], 
    4: h'71776572747975696F706C6B6A686766'
}

This gives us the CoAP response:

-----JoinResponse for Node-----

CoAP-Message:{
    version=1 , type=1 , TKL=4, Code=68, messageID=47789 ,token (size 4):   0xDE 0xAD 0xBA 0xBE

    options=    
        CoAP-Opt: type=55 , value (size 12):    0x61 0x62 0x62 0x61 0x2E 0x69 0x73 0x2E 0x62 0x61 0x63 0x6B

    payload (size 50):  0xA3 0x02 0x83 0x01 0x02 0x50 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 0x99 0xAA 0xBB 0xCC 0xDD 0xEE 0xFF 0x11 0x03 0x82 0x42 0xA1 0xB1 0x1A 0x01 0xDF 0xE3 0x90 0x04 0x50 0x71 0x77 0x65 0x72 0x74 0x79 0x75 0x69 0x6F 0x70 0x6C 0x6B 0x6A 0x68 0x67 0x66 
}

serialized coap (size 73):

0x54 0x44 0xBA 0xAD 0xDE 0xAD 0xBA 0xBE 0xDC 0x2A 0x61 0x62 0x62 0x61 0x2E 0x69 0x73 0x2E 0x62 0x61 0x63 0x6B 0xFF 0xA3 0x02 0x83 0x01 0x02 0x50 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 0x99 0xAA 0xBB 0xCC 0xDD 0xEE 0xFF 0x11 0x03 0x82 0x42 0xA1 0xB1 0x1A 0x01 0xDF 0xE3 0x90 0x04 0x50 0x71 0x77 0x65 0x72 0x74 0x79 0x75 0x69 0x6F 0x70 0x6C 0x6B 0x6A 0x68 0x67 0x66

and when secured by OSCORE:

OSCORE-Message:{
    version=1 , type=1 , TKL=4, Code=68, messageID=47789 ,token (size 4):   0xDE 0xAD 0xBA 0xBE

    options=    
        CoAP-Opt: type=21 , value (size 0):

        CoAP-Opt: type=55 , value (size 12):    0x61 0x62 0x62 0x61 0x2E 0x69 0x73 0x2E 0x62 0x61 0x63 0x6B

    payload (size 60):  0x70 0x3A 0xFB 0x0F 0xC6 0xB3 0x95 0x8E 0x29 0x58 0x6E 0x09 0x0C 0xE5 0xA7 0x5D 0x64 0x86 0xB3 0x61 0xBF 0x94 0xAB 0x24 0x7B 0xDD 0x56 0x85 0x55 0x9F 0x43 0xAC 0x47 0x41 0x9E 0xA0 0xD9 0x60 0x73 0xDD 0x8A 0x32 0x58 0xF8 0xCF 0x85 0xD5 0x29 0xFA 0x5D 0xC1 0x6E 0x82 0x4A 0x99 0xEC 0x3D 0x8A 0x77 0x63 
}

serialized oscore (size 85):

0x54 0x44 0xBA 0xAD 0xDE 0xAD 0xBA 0xBE 0xD0 0x08 0xDC 0x15 0x61 0x62 0x62 0x61 0x2E 0x69 0x73 0x2E 0x62 0x61 0x63 0x6B 0xFF 0x70 0x3A 0xFB 0x0F 0xC6 0xB3 0x95 0x8E 0x29 0x58 0x6E 0x09 0x0C 0xE5 0xA7 0x5D 0x64 0x86 0xB3 0x61 0xBF 0x94 0xAB 0x24 0x7B 0xDD 0x56 0x85 0x55 0x9F 0x43 0xAC 0x47 0x41 0x9E 0xA0 0xD9 0x60 0x73 0xDD 0x8A 0x32 0x58 0xF8 0xCF 0x85 0xD5 0x29 0xFA 0x5D 0xC1 0x6E 0x82 0x4A 0x99 0xEC 0x3D 0x8A 0x77 0x63

Here the message just NEEDS to be fragmented one way or another, no questions asked.

( Nota bene:

Of course all of this is just preliminary results based on a test implementation that probably has a few bugs, however the change to the message size once those are ironed-out shouldn't change much and the global idea will quite probably stay the same)

Comments (12)

  1. JRTI reporter

    Just to clarify, in the Join response, the option with number 55 is a hypothetical stateless proxy option, whose value is "abba.is.back" (size 12 ), the option number 55 was arbitrarily chosen because it was free.

    And second thing the payload of the request is the cbor:

    {
        5: h'6E657431'
    }
    

    And the payload of the response is the cbor:

    {
        2: [1, 2, h'112233445566778899AABBCCDDEEFF11'], 
        3: [h'A1B1C1D1E1F1A2B2', 31450000], 
        4: h'71776572747975696F706C6B6A686766'
    }
    
  2. Mališa Vučinić

    @JRTI ,

    Some points on where you could save bytes in the stack:

    (denoting with [AP] action points for myself)

    • Auxiliary Security Header (ASH) in your examples is 6 bytes. The ASH in TSCH mode of 802.15.4 should be 2 bytes long. You are probably including unnecessarily the 4-byte Frame Counter. See https://tools.ietf.org/html/rfc8180#appendix-A.4

    • In your L2 header, you are missing at least the destination PAN ID, additional 2 bytes.

    • For L2 security purposes, in OpenWSN we typically use 4-byte MIC, allowing us to save 4 additional bytes. With this MIC length and the previous points, the total L2 overhead should be 29 bytes. I understand that for certain applications 4-byte MIC may not be enough and that you may need to use the 8-byte MIC, increasing this overhead to 33 bytes.

    • IPv6/6LoWAN overhead varies if the packet is going up or down the mesh.

    Now, regarding the CoAP messages:

    • The CoAP token from the pledge to the JP is not necessary as the pledge should only send one request at a time and can therefore match the response purely based on the MID and the IPv6 address/port. You can simply send a 0-length token and save 4 bytes there. In case of the message from the JP to the JRC, the question of the token is a bit more complex. We are exploring the possibility of using the Token field to carry the Stateless-Proxy option, as part of another WGLC review of the document [AP] . Assuming a Stateless-Proxy option is added to the forwarded message, the token from the JP to the JRC is also not needed as the response is statelessly forwarded and not matched to the request at JP.
    Therefore we are still ~ 20 bytes away from the actual limit here, but if 16-byte IPv6 addresses (instead of 8-byte) had been used this message would be on the brink of needing fragmentation.
    
    • Note that the IPv6 packet from the pledge to the JP always uses link-local addressing, meaning that it will always be compressed down to 8 bytes using stateful 6LoWPAN IPHC.

    • Once the JP forwards the request to the JRC, you are correct in that it removes the Uri-Host (6tisch.arpa), adding Stateless-Proxy. In this message forwarded by the JP, EUI-64 of the pledge is present twice: as OSCORE kid-context and as Stateless-Proxy option value. These are however, semantically quite different so I would need to explore if it would be possible to optimize it out by carrying EUI-64 of the pledge only within one of them. I will open a thread with this issue on the ML [AP].

    Now, on to the Join Response:

    • I don't understand the comment where the address of the JRC could be less than 16 bytes? We carry this address only if the JRC is not co-located with the RPL root and therefore does not share the network prefix with the nodes. Could you elaborate what you meant and maybe provide a bit more details on your use case?

    • Your assigned short address parameter in CBOR Configuration object is 8 bytes? Pledge started with 8-byte long EUI-64 and the purpose of this parameter is for the JRC to assign it a shorter address. In 802.15.4, the short address is 2-bytes long.

    • Encoding address lease time in seconds indeed seems to require much more bytes than needed. We could round this to minutes, or maybe even hours. I will bring this up on the ML [AP].

  3. Mališa Vučinić

    Regarding the use of BLOCK-WISE, indeed we haven't considered this as we worked with the assumption of fragmentation being done on the 6LoWPAN layer. There is ongoing work in IETF 6LO on making the fragmentation efficient and stateless for intermediary IP routers.

    However, I do believe that it would be important to support the fragmentation via BLOCK-WISE as well, for the use cases where IP is not the underlying transport. I don't think that the solution where a third message is added to the protocol (CoAP ACK for the CON Join Response) is ideal from the communication overhead point of view, but I will explore this further, bring it up on the ML and discuss it during the IETF102 presentation [AP].

  4. JRTI reporter

    Hmm, relying on lower level fragmentation would indeed save some overhead and make more sense, thanks !

    Do you have a specific reason to ask for the JRC address to be the full 16 bytes instead of either 2,8 or 16 bytes if it is present in the configuration object though ? It would greatly reduce the cost of adding this optional parameter.

  5. Mališa Vučinić

    The JRC address is by definition an IPv6 address, i.e. 16 bytes in length. The part that I am missing is what kind of address do you assume that is 2 bytes or 8 bytes long?

  6. JRTI reporter

    Considering the constrained nature of the 802.15.4 networks, there are ways to route packets inside the network by omitting certain parts of the full address from the messages. Here if you look under "SAM: Source Address Mode:"

         if SAC=0:
         00:  128 bits.  The full address is carried in-line.
    
         01:  64 bits.  The first 64-bits of the address are elided.
            The value of those bits is the link-local prefix padded with
            zeros.  The remaining 64 bits are carried in-line.
    
         10:  16 bits.  The first 112 bits of the address are elided.
            The value of the first 64 bits is the link-local prefix
            padded with zeros.  The following 64 bits are 0000:00ff:
            fe00:XXXX, where XXXX are the 16 bits carried in-line.
    
         11:  0 bits.  The address is fully elided.  The first 64 bits
            of the address are the link-local prefix padded with zeros.
            The remaining 64 bits are computed from the encapsulating
            header (e.g., 802.15.4 or IPv6 source address) as specified
            in Section 3.2.2.
    

    Are there specific requirements for only providing the full IPv6 address ?

  7. Mališa Vučinić

    Ok, I see now what you mean: you are proposing to leverage the stateful IPv6 context compression from RFC6282 during the transport of the JRC's address as part of CoJP.

    For this to work, we would need to address a couple of issues, one of which is how do you get that context installed on the pledge before it actually joined. Some optimizations could indeed be possible, e.g. in the case when JRC is not co-located with the RPL root, but JRC does share the network prefix with the pledge. If you have another use case in mind, please let me know as it would help the working group judge the importance of making this optimization. To be discussed at IETF102 [AP].

  8. JRTI reporter

    Oh sorry I actually hadn't seen your detailed message from 4 days ago, let me first answer it.

    You make a lot of good points, and indeed up to 10 bytes with only a 4-byte MIC +- the varying IPv6/6LoWPAN overhead could be saved for the Join request/response (unless I'm mistaken, we do need the token for the update requests/responses since the JRC can send the update message at several nodes at a time and both CoAP and OSCORE need it to match request and response).
    And you're right on the short address, truth be told I don't even remember what led me to choose 8 bytes here instead of 2.

    In the end saving 16 bytes does make it possible for the join response even with all the options present to be transported in one message (as long as the keyset is only one key long), and even with an 8 byte MIC it should work out (although barely) which was my main concern.

    Regarding the 16 vs 8 / 2 byte long IPv6 address I was actually talking about the stateless compression not the statefull one, but I completely overlooked the fact that the JRC would have a different prefix and that it wouldn't be applicable.
    Some stateless optimization mechanism -making it so that only the 6LBR needs to know the full 16-byte long address, and all the other nodes would think it a 2-byte address- can probably be devised, but my use-case would not need it and it seems pretty far for the goal of this draft so I do not think it is that critical.

    Thank you again for addressing all my points !

  9. Log in to comment