This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC1310: Sensor/Collector Understanding keys and security

Part Number: CC1310

Hi,

The example uses Key Identifier Mode = 3. (STATIC CONST ApiMac_keyIdMode_t secKeyIdMode = ApiMac_keyIdMode_8)

In this mode a "key source" of 8 bytes are sent in all security enabled frames - in addition to a "key index". This adds a lot of overhead to the transmitted frames.

By instead using Key Identifier Mode = 1 (STATIC CONST ApiMac_keyIdMode_t secKeyIdMode = ApiMac_keyIdMode_1), the key source is not transmitted in every frame (solely key index is sent), and instead a default key source is used.

It looks to me that the default key source is: #define JDLLC_DEFAULT_KEY_SOURCE {0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33}

However, there is another struct with almost the same data in it (are they somehow related?):

static CONST ApiMac_keyIdLookupDescriptor_t keyIdLookupList[] =
{
{
/* Key identity data */
{ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x03 }, // (looks like 0x03 is related to: STATIC CONST uint8_t secKeyIndex = 3;)
0x01 /* 9 octets */ // (looks like this is related Key Identifier Mode 2 and 3.
}
};

I am not sure what the default key source actually is. And what the "key index" is. It looks to me that the final key used for encryption of packets is derived from the key source, key index, (possibly addresses) and the KEY_TABLE_DEFAULT_KEY. Could someone clarify this?

As of now, it looks like the example does not alter the default key source or the key index in transmitted frames. So what is the point of having them in the frames, when they can be defined as constants, more like "Key Identifier Mode = 1"?

And what is the use case for the "key index". STATIC CONST uint8_t secKeyIndex = 3; Can multiple key index values be used in the same operating network?

Thanks

  • Hi,

    The security side of the application is a bit complex, and requires a slightly long explanation so I will come back to you on Monday when I have a bit more time to dig into this subject.

  • Hi Hector,
    Do you have some more info regarding the security settings? Optimally I would like to use the security mode which requires the least RAM resources per sensor, minimum frame size, and I would like to have 2 security ID's. The collector must be able to handle both keys. The application layer code must also be able to determine which security key is used when it receives frames from sensors. The collector must also be able to choose which key to use when it transmits frames (by changing the key in the device table or something...).

    Thanks.
  • Hi HC,
    Sorry for the delay.
    JDLLC_DEFAULT_KEY_SOURCE and keyIdLookupList are related. JDLLC_DEFAULT_KEY_SOURCE sets up the default key that this device is using on the outgoing packets, keyIdLookupList is the look up list where the device will match the incoming frames with a source key to find the security key and decrypt the packets.

    To give you some perspective,
    Key Source: is used to identify the source of a group key. This key source could be set to the extended address of the Collector to identify that the collector originated the security key. Also this key source field is used when the device is looking for a security key.
    Key Index: this is used as a unique ID for a specific security key under a keySource. So for example if the Collector generates multiple security keys then all these keys will be under the same KeySource but they will have to be stored with different KeyIndex if they are all being used of course.

    The actual things that are used for encryption and decryption are the device's extended address and the KEY_TABLE_DEFAULT_KEY. Therefore in order to be able to decrypt packets from a specific device, first you need to have this device in your device list and you need to know the extended address of this device.


    Using key ID Mode 3 can be helpful in a situation where you have multiple devices generating keys and using different keys in the network. This way the receiving device and find the security key that was used to encrypt a packet based on the key source and the key index.
    In the way that the out of the box examples are done it would make sense to use key ID mode = 1 as you mentioned since we are using only 1 predefined key.

    If you want to use multiple keys you can use the API ApiMac_secAddKeyInitFrameCounter to register multiple keys, you can see how this is used in Cllc_securityInit also make sure you use different values for replaceKeyIndex for each address you are using.
    Also make sure you set the attribute "ApiMac_securityAttribute_keyTableEntries" to 2 to specify that you need 2 table entries.

    Also there are other defines in api_mac.h that you probably will need to take a look at such as MAX_KEY_TABLE_ENTRIES.

    I hope this helps.
  • Thanks Hector. I am trying to carefully read and understand the key setup.

    One small thing I do not understand is the following constant cllc.c:

    STATIC CONST ApiMac_keyDescriptor_t keyTable[] =
        {
          {
            (ApiMac_keyIdLookupDescriptor_t *)keyIdLookupList,
            KEY_ID_LOOKUP_ENTRIES,
            (ApiMac_keyDeviceDescriptor_t *)keyDeviceList,
            KEY_DEVICE_TABLE_ENTRIES,
            (ApiMac_keyUsageDescriptor_t *)keyUsageList,
            KEY_USAGE_TABLE_ENTRIES,
            KEY_TABLE_DEFAULT_KEY,
            0 /* frame counter */
          }
        };

    The keyTable is only referenced in Cllc_securityInit: memcpy(secInfo.key, keyTable[0].key, APIMAC_KEY_MAX_LEN). The only field used is "key", most of the remaining fields are not touched in the application. As an example, keyDeviceList, is not referenced anywhere. These "dead" constants also make it difficult to grasp the inner workings of the example code...

  • Hi,
    Yes, I agree with you there are some unused constants sprinkled around the application. We are trying to clean this up and potentially adding extra materials to help implement other security configurations.
    One of the reason why there are some unused variables is mainly because of changes that have happened in the security implementation and the older implementation was never fully cleaned up.

    My advice as of now would be to just pay attention to the constants and variables that are actually being used and ignore the ones that are never referenced.
  • Hi Hector,

    OK, I am now ignoring and commenting out the unused constants etc. This is my current understanding:

    In Cllc_securityInit I can define multiple ApiMac_secAddKeyInitFrameCounter(&secInfo) with different replaceKeyIndex. Here I can define unique KEY_TABLE_DEFAULT_KEY keys. secInfo.lookupData contains the 9 byte Key identity data (including a key index). I am not totally sure why this is defined here... I assume it's for incoming frames in some way. I also assume that secInfo.lookupData must be different for each ApiMac_secAddKeyInitFrameCounter(&secInfo) definition? In that case, must both the first 8 bytes be different, and the last byte (key index)? 

    How will the MAC layer understand which instance of ApiMac_secAddKeyInitFrameCounter(&secInfo) (i.e. replaceKeyIndex = 0 and replaceKeyIndex = 1) to use for incoming frames?

    In Cllc_securityInit, ApiMac_securityAttribute_defaultKeySource defines the default key source for outgoing frames when key ID mode 0x01 is used. Hence, in Cllc_securityFill (for outgoing frames) the data loaded into pSec->keySource is ignored for key ID mode 0x01 - but is used for key ID mode 0x03. I can choose whatever key index to use (pSec->keyIndex = secKeyIndex) for both key ID modes.

    Cllc_addSecDevice is called when new devices are joining (the frame which invoked this function however, is NOT secured - hence we do not know which key index and default key source that this new device will use for future frames, at this stage). Finally ApiMac_secAddDevice(&device) is called, and "security" data for the specific device is stored in the MAC so that future frames can be decrypted(?) The key identity data is loaded into device.keyIdLookupData. So here the key identity data is stored for each unique device. But why is the same data also loaded in ApiMac_secAddKeyInitFrameCounter(&secInfo) as mentioned above? Are the combination of key identity data and key index used as a lookup inside the deep MAC layer to see if the combination is "stored" in any of the ApiMac_secAddKeyInitFrameCounter(&secInfo) instances - and if so, they will be successfully decrypted?

  • Hi Hector,

    Which MAC PIB version is implemented? 2006 or 2011 or something else? The 2011 version is described below:

    It looks like I should be able to define a list (array) of keyIdLookupList (the example array is size 1). So this array should be definable for one key descriptor. It also seems that ApiMac_secAddKeyInitFrameCounter is used to define one such key descriptor. The secInfo.lookupData is the pointer to the keyIdLookupList array. However: It seems that this array can only be of size 1. So, is the ti 15.4 stack limited to only have one key id lookup instance for each key descriptor?

  • You are not limited to only 1 lookup id, to add more lookup IDs other than the one that you add when you register the key you can use the following API 

    ApiMac_mlmeSetSecurityReqStruct(ApiMac_securityAttribute_keyIdLookupEntry, &lookUpEntry);

    Also see below of what changes you would have to do to use multiple keys. this example uses 2 different keys for the same device.

    In config.h define 

    #define KEY_TABLE_DEFAULT_KEY2 {0x55, 0x12, 0x67, 0x78, 0x9a, 0xbc, 0xde, 0xde,\
                                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}

    In cllc.c

    #define KEY_TABLE_ENTRIES 2
    
    ...
    
    STATIC CONST ApiMac_keyIdLookupDescriptor_t keyIdLookupList[] =
    {
      {
        /* Key identity data */
        { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x03 },
        0x01 /* 9 octets */
      },
      {
      /* Key identity data */
      { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x04 },
      0x01 /* 9 octets */
      }
    };
    
    uint8_t newKey[] = KEY_TABLE_DEFAULT_KEY2;
    
    ...
    
    
    STATIC CONST ApiMac_keyIdMode_t secKeyIdMode = ApiMac_keyIdMode_1;
    
    ...
    
    
    void Cllc_securityInit(uint32_t frameCounter)
    {
        if(macSecurity == true)
        {
    ...
    //add the second key to the table list
            memcpy(secInfo.key, newKey, APIMAC_KEY_MAX_LEN);
            secInfo.frameCounter = frameCounter;
            secInfo.replaceKeyIndex = 1;
            secInfo.newKeyFlag = true;
            secInfo.lookupDataSize = APIMAC_KEY_LOOKUP_LONG_LEN;
            memcpy(secInfo.lookupData, keyIdLookupList[1].lookupData,
                   (APIMAC_MAX_KEY_LOOKUP_LEN));
    
            ApiMac_secAddKeyInitFrameCounter(&secInfo);
    
    ...
    
    
    ApiMac_status_t Cllc_addSecDevice(uint16_t panID, uint16_t shortAddr,
                             ApiMac_sAddrExt_t *pExtAddr, uint32_t frameCounter)
    {
        if(macSecurity == true)
        {
            ApiMac_secAddDevice_t device;
            uint8_t keyIndex = 0;
    
            device.panID = panID;
            device.shortAddr = shortAddr;
            memcpy(device.extAddr, pExtAddr, sizeof(ApiMac_sAddrExt_t));
            device.frameCounter = frameCounter;
            device.exempt = false;
    
            /* get the key lookup information from the initial loaded key */
            device.keyIdLookupDataSize = keyIdLookupList[keyIndex].lookupDataSize;
            memcpy(device.keyIdLookupData, keyIdLookupList[keyIndex].lookupData,
                   (APIMAC_MAX_KEY_LOOKUP_LEN));
    
            device.uniqueDevice = false;
            device.duplicateDevFlag = false;
            ApiMac_secAddDevice(&device);
            keyIndex++;
            memcpy(device.keyIdLookupData, keyIdLookupList[keyIndex].lookupData,
                           (APIMAC_MAX_KEY_LOOKUP_LEN));
            return(ApiMac_secAddDevice(&device));
        }
        else
        {
            return(ApiMac_status_success);
        }
    }
    
    
    
    ...
    
    
      #define MAX_KEY_ID_LOOKUP_ENTRIES         2
    
      #define MAX_NODE_KEY_ENTRIES              2
    
    

  • Thanks Hector, it worked.

    There is one variation that does not work.

    1. In Collector I add both keys in the init.

    2. In Sensor I add both keys in the init.

    3. In xxxx_addSecDevice I add a device twice (like in your example) for both collector and sensor.

    I randomly use key index 3 and 4 for outgoing frames  in both Sensor and Collector - this works!

    4. Later (after some frames) I write the SAME second key in Sensor during run-time - I just rewrote the exact same data (normally I would write a new key etc...):

    void Jdllc_securityAddNetworkKeyTable(ApiMac_securityNetworkAESkey_t *pKey, uint32_t frameCounter)
    {
        ApiMac_secAddKeyInitFrameCounter_t secInfo;
    
        memcpy(secInfo.key, pKey->key, APIMAC_KEY_MAX_LEN);
        secInfo.frameCounter = frameCounter;
        secInfo.replaceKeyIndex = 1; // index for network key table.
        secInfo.newKeyFlag = true;
        secInfo.lookupDataSize = APIMAC_KEY_LOOKUP_LONG_LEN;
        memcpy(secInfo.lookupData, keyIdLookupList[1].lookupData,
               (APIMAC_MAX_KEY_LOOKUP_LEN));
        ApiMac_secAddKeyInitFrameCounter(&secInfo);
    }

    Now, it does not work...

    Sensor can transmit with keyIndex=4 ( dataCnfCB->status: 00).

    Sensor has issues transmitting with keyIndex=3 and reports dataCnfCB->status: e9. The Collector receives 4 instances of this message.

    Sensor reports many macAckFailures.

    Sensor reports many rxDecryptFailures.

    Sensor does not receive any frames from Collector when Collector uses keyIndex=3.

    I use secLevel = ApiMac_secLevel_enc (so frame numbering should not be an issue).

    So basically my question is: How can I add or update the security keys after initialization?

  • As far as I know, updating the keys after initialization should not be a problem. Let me run a couple of tests and see what I find out.
  • Thanks. The function is originally called from "static void dataIndCB(ApiMac_mcpsDataInd_t *pDataInd)". I.e. the key originates from from the Collector. I just wanted to inform in case there may be some issues related to this (thread?).
  • Hello Hector,

    I found a relation for the anomaly. It seems that if you call Jdllc_securityInit after Jdllc_addSecDevice, it screws up.

    I did the following test in jdllc.c, static void wsAsyncIndCb(ApiMac_mlmeWsAsyncInd_t *pData):

    #ifdef FEATURE_MAC_SECURITY
            /* add parent to security device table */
            Jdllc_addSecDevice(pData->srcPanId, FH_COORD_SHORT_ADDR,
                               &(pData->srcAddr.addr.extAddr), pData->frameCntr);
            // test ---
            uint32_t frameCounter = 0;
            Ssf_getFrameCounter(NULL, &frameCounter);
            Jdllc_securityInit(frameCounter);
            // test ---
    #endif /* FEATURE_MAC_SECURITY */

    (The Jdllc_securityInit loads both keys). The above caused the same issue as stated in previous post. Moving the "// test ---" section to above Jdllc_addSecDevice does not cause any errors. I am not sure why this is a problem. The only potential workaround (if it works) I can think of is to make a copy of security device data, then delete it, then add the key(s), and then add the security device data again...

  • Just to confirm that I understand what you are doing. You are trying to add a new key in the sensor after it has been "authenticated" by the collector?
    Is my understanding correct?
  • In the "application-meaningless test case" initially described, I Initially add two keys in both collector and sensor: Factory_key and Network_key. These are added in Jdllc_securityInit. The two keys are differentiated with keyIndex = 3 and keyIndex = 4 for outgoing packets (and naturally the corresponding key is used for decoding when a packet is received). Both the sensor and collector use both keyIndexes when they transmit frames (they alternate). Both the sensor and collector can successfully receive (and transmit) every frame.

    Then when the sensor has sent many temperature data frames, I decide to update the network key in the sensor with the same exact key as already written into its PIB. Then it does not work. I just simply rewrite the exact same data.

    The test case in my previous message is trying to figure what causes this error to occur. And what I found is that you cannot update both (or one) security keys (network and factory) in the sensor project with the exact same data after you have added the collector-device to the security device list in the sensor project.

    The actual use case would be to initially only have the factory key initialized in the sensor when powered up for the first time. When the sensor associates with the collector, the collector will add the sensor in its security device list with two identity keys, namely the identity keys which correspond to factory key and network key - even though the sensor at this point has no knowledge about the network key. But this is OK, since both the sensor and collector will use the keyIndex corresponding to the factory key for frames. Then a bit later the collector will transmit the network key to the sensor. Then the sensor will add this key into the key table - and the sensor will also add the second entry into its security device list, namely the collector-device with key identity data corresponding to the network key (even though this could have been added initially since the key identity data for the network key is known beforehand). Then both the sensor and collector will start using the key index corresponding to the network key.

  • Ok, now i have a clearer picture of what you are trying to do. If you want to update an existing key and transfer all the devices from the previous key to the new one you just need to make sure that when you add the new key you set newKeyFlag = true. this will grab the device table in the index specified by replaceKeyIndex and assign it to the new key. See code below.

            ApiMac_secAddKeyInitFrameCounter_t secInfo;
    
            memcpy(secInfo.key, keyTable[localKeyIndex].key, APIMAC_KEY_MAX_LEN);
            secInfo.frameCounter = frameCounter;
    //Note that this key index refers to the index in the key list and not the keyIndex that will be used over the air for key lookup
            secInfo.replaceKeyIndex = localKeyIndex;  
            secInfo.newKeyFlag = true; // make sure that this is set to true if you want to inherit the device list from the previous key
            secInfo.lookupDataSize = APIMAC_KEY_LOOKUP_LONG_LEN;
            memcpy(secInfo.lookupData, keyIdLookupList[localKeyIndex].lookupData,
                   (APIMAC_MAX_KEY_LOOKUP_LEN));
    
            ApiMac_secAddKeyInitFrameCounter(&secInfo);

  • I have set the newKeyFlag = true (it was so initially as well). It still does not work. Could this be because there are 2 entries per device in the "security device list"?
  • Ill take a look at this today and let you know what I find.
  • I am seeing the same issue you are seeing, I am currently looking into it to see what is causing it
  • Thank you for looking into this. I am extremely happy that you could reproduce the anomaly! If you are rewriting/updating both keys in sequence, also try to change the sequence as well. In one of the cases it did not "even" return a success. And say hallo to Kris and Tarjei :-)

    Currently I am having issues to read ApiMac_mlmeGetSecurityReqStruct(ApiMac_securityAttribute_keyDeviceEntry, &secPibKeyDevEnt) and ApiMac_mlmeGetSecurityReqStruct(ApiMac_securityAttribute_deviceEntry, &secPibDevEnt). It reads sometimes, and then the whole app stops. I am checking if this is memory related.

    HC

  • Hei HC,

    If you just need to update the key then you can use the code below 

    ApiMac_securityPibKeyEntry_t newKeyEntry;
    newKeyEntry.keyIndex = 1;
    newKeyEntry.frameCounter = 0;
    memcpy(myKey.keyEntry, newKey, APIMAC_KEY_MAX_LEN);
                    
    ApiMac_mlmeSetSecurityReqStruct(ApiMac_securityAttribute_keyEntry, &newKeyEntry);

    For now probably stay away from the API "ApiMac_secAddKeyInitFrameCounter" when trying to update a key because I am seeing some strange behavior which I still need to confirm if it is a bug or just not accurate documentation. Only use "ApiMac_secAddKeyInitFrameCounter" when you need to add a key to a specific index instead of updating one.

    I will also look into getting the device entry and key device entry to see if I can reproduce the same behavior you are seeing.

  • Hei Hector,

    Thanks, I will test it out.

    I did some debug testing as well:

    ** Print Security Details **
      -- ApiMac_securityAttribute_keyDeviceEntry:
        KeyDevList[0]=0: BL=0, DDH=0, UD=0
        KeyDevList[0]=1: BL=0, DDH=65535, UD=0
        KeyDevList[0]=2: BL=0, DDH=65535, UD=0
        KeyDevList[1]=0: BL=0, DDH=0, UD=0
        KeyDevList[1]=1: BL=0, DDH=65535, UD=0
        KeyDevList[1]=2: BL=0, DDH=65535, UD=0
      -- ApiMac_securityAttribute_deviceEntry:
        devIndx=0: shAddr=43707, panID=1, exempt=0, frCntr=134, keyIdx=0
        devIndx=1: shAddr=65535, panID=65535, exempt=0, frCntr=0, keyIdx=65535
        devIndx=2: shAddr=65535, panID=65535, exempt=0, frCntr=0, keyIdx=65535
    ** End **
    Factory key loaded. secInfo.replaceKeyIndex=0
    Network key loaded. secInfo.replaceKeyIndex=1
    ** Print Security Details **
      -- ApiMac_securityAttribute_keyDeviceEntry:
        KeyDevList[0]=0: BL=0, DDH=0, UD=0
        KeyDevList[0]=1: BL=0, DDH=65535, UD=0
        KeyDevList[0]=2: BL=0, DDH=65535, UD=0
        KeyDevList[1]=0: BL=0, DDH=0, UD=0
        KeyDevList[1]=1: BL=0, DDH=65535, UD=0
        KeyDevList[1]=2: BL=0, DDH=65535, UD=0
      -- ApiMac_securityAttribute_deviceEntry:
        devIndx=0: shAddr=43707, panID=1, exempt=0, frCntr=0, keyIdx=1
        devIndx=1: shAddr=65535, panID=65535, exempt=0, frCntr=0, keyIdx=65535
        devIndx=2: shAddr=65535, panID=65535, exempt=0, frCntr=0, keyIdx=65535
    ** End **
    Set keyIdx = 0
    ** Print Security Details **
      -- ApiMac_securityAttribute_keyDeviceEntry:
        KeyDevList[0]=0: BL=0, DDH=0, UD=0
        KeyDevList[0]=1: BL=0, DDH=65535, UD=0
        KeyDevList[0]=2: BL=0, DDH=65535, UD=0
        KeyDevList[1]=0: BL=0, DDH=0, UD=0
        KeyDevList[1]=1: BL=0, DDH=65535, UD=0
        KeyDevList[1]=2: BL=0, DDH=65535, UD=0
      -- ApiMac_securityAttribute_deviceEntry:
        devIndx=0: shAddr=43707, panID=1, exempt=0, frCntr=0, keyIdx=0
        devIndx=1: shAddr=65535, panID=65535, exempt=0, frCntr=0, keyIdx=65535
        devIndx=2: shAddr=65535, panID=65535, exempt=0, frCntr=0, keyIdx=65535
    ** End **

    In the init, the two keys were initially loaded in this sequence: 

    Factory key loaded. secInfo.replaceKeyIndex=0
    Network key loaded. secInfo.replaceKeyIndex=1

    In the first "Print Security Details" notice that keyIdx = 0. Then I rewrite the factory and network key (in that order). Notice that keyIdx has changed to 1. Then I manually set the keyIdx to 0. Now it works! I am not sure what keyIdx does. But it seems to me that this variable somehow works like this: "I will only accept to use keys from the key table with table-key-indexes (replaceKeyIndex) higher than or equal to keyIdx"...

    Another thing to notice is that the frCntr is reset to 0. This could allow for replay attacks. So when updating keys, both frCntr and keyIdx should be left untouched.

    I found the issue regarding ApiMac_securityAttribute_deviceEntry. I was my fault. The ApiMac_securityPibDeviceEntry_t has a pointer reference to ApiMac_frameCntr_t which also needs to be allocated and set to point to the pointer reference in ApiMac_securityPibDeviceEntry_t. To be safe I also used Ssf_malloc to allocate the structs.

    HC

  • I took a closer look at ApiMac_secAddKeyInitFrameCounter and there is a bug... If you use this API to update a key after devices have joined the network there is a chance that it will override the key indexes for devices in the device table.

    So if you need to update/change a key after you have already added devices to the device table then you should use the code that I posted in my previous post instead of using ApiMac_secAddKeyInitFrameCounter