This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Overflowing CCM scratchpad

The scratchpad is overflowing when generating a keystream prior to a CCM decrypt operation.

The size of the scratchpad is calculated according to the formula in the product specification:

"A space of 43 bytes, or (16 + MAXPACKETSIZE) bytes, whatever is largest, must be reserved in RAM."

In this case we have a payload of 64 bytes which gives MAXPACKETSIZE = 64 + header + length + RFU + MIC = 64 + 1 + 1 + 1 + 4 = 71.

This gives a scratchpad size of 16 + 71 = 97

To illustrate the problem a scratchpad size of 140 has been chosen.

 

A decryption operation is attempted as follows:

void my_decrypt(void)
{
   static uint8_t mi_unencrypted[UNENCRYPTED_PACKET_LEN];

   static uint8_t mi_encrypted[ENCRYPTED_PACKET_LEN] =
   {
      53,
      64,
      0,
      123, 231, 123, 231, 123, 231, 123, 231,
      123, 231, 123, 231, 123, 231, 123, 231,
      123, 231, 123, 231, 123, 231, 123, 231,
      123, 231, 123, 231, 123, 231, 123, 231,
      123, 231, 123, 231, 123, 231, 123, 231,
      123, 231, 123, 231, 123, 231, 123, 231,
      123, 231, 123, 231, 123, 231, 123, 231,
      123, 231, 123, 231, 123, 231, 123, 231,
      123, 231, 123, 231
   };

   static uint8_t mi_key[KEY_LEN] =
   {
      87, 78, 87, 78, 87, 78, 87, 78, 87, 78, 87, 78, 87, 78, 87, 78
   };

   /* Copy encryption key to ccm configuration. */
   memcpy((void *)ccm_cfg.key, (void *)mi_key, KEY_LEN);

   ccm_cfg.iv[0] = 123;
   ccm_cfg.iv[1] = 231;
   ccm_cfg.iv[2] = 123;
   ccm_cfg.iv[3] = 231;
   ccm_cfg.iv[4] = 123;
   ccm_cfg.iv[5] = 231;
   ccm_cfg.iv[6] = 123;
   ccm_cfg.iv[7] = 231;

   decrypt(mi_encrypted, mi_unencrypted);
}

static void decrypt(uint8_t * p_data, uint8_t * p_result)
{
   /* Set up mode register for decryption. */
   NRF_CCM->MODE =   (CCM_MODE_LENGTH_Extended  << CCM_MODE_LENGTH_Pos)     |
                     (CCM_MODE_DATARATE_2Mbit   << CCM_MODE_DATARATE_Pos)   |
                     (CCM_MODE_MODE_Decryption  << CCM_MODE_MODE_Pos);

   /* Set up input and output pointers. */
   NRF_CCM->INPTR    = (uint32_t)p_data;
   NRF_CCM->OUTPTR   = (uint32_t)p_result;

   /* Decrypt. */
   cipher();
}

static void cipher(void)
{
   /* Point to CCM configuration. */
   NRF_CCM->CNFPTR = (uint32_t)&ccm_cfg;

   /* Set up scratchpad. */
   NRF_CCM->SCRATCHPTR = (uint32_t)&scratchpad;

   /* Set up shortcut between key stream generated event and encrypt task. */
   //NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Enabled << CCM_SHORTS_ENDKSGEN_CRYPT_Pos;

   /* Turn on interrupts. */
   sd_nvic_EnableIRQ(CCM_AAR_IRQn);
   sd_nvic_SetPriority(CCM_AAR_IRQn, CCM_INTERRUPT_PRIORITY);

   NRF_CCM->INTENSET =  (CCM_INTENSET_ENDKSGEN_Set << CCM_INTENSET_ENDKSGEN_Pos) |
                        (CCM_INTENSET_ENDCRYPT_Set << CCM_INTENSET_ENDCRYPT_Pos) |
                        (CCM_INTENSET_ERROR_Set    << CCM_INTENSET_ERROR_Pos);

   /* Enable CCM. */
   NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Enabled;

   /* Create crypt semaphore. */
   ccm_semaphore = xSemaphoreCreateBinaryStatic(&ccm_semaphore_buffer);

   assert(NULL != ccm_semaphore);

   /* Generate key stream. This is used to generate the MIC.
    * This step is required for encryption to work. Encryption/ decryption will automatically
    * start when this completes.
    */
   NRF_CCM->TASKS_KSGEN = 1U;

   /* Wait for encryption/decryption to complete. */
   assert(pdTRUE == xSemaphoreTake(ccm_semaphore, portMAX_DELAY));

   /* Turn off CCM. */
   NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Disabled;

   /* Delete semaphore as it is no longer needed. */
   vSemaphoreDelete(ccm_semaphore);
}

void CCM_AAR_IRQHandler(void)
{
   BaseType_t xHigherPriorityTaskWoken = pdFALSE;

   if (1U == NRF_CCM->EVENTS_ENDKSGEN)
   {
      /* Key stream has been generated. */

      /* Clear event. */
      NRF_CCM->EVENTS_ENDKSGEN = 0U;
   }
   else if (1U == NRF_CCM->EVENTS_ENDCRYPT)
   {
      /* Clear event. */
      NRF_CCM->EVENTS_ENDCRYPT = 0U;

      /* Indicate that encryption / decryption is complete. */
      assert(pdTRUE == xSemaphoreGiveFromISR(ccm_semaphore, &xHigherPriorityTaskWoken));

      portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
   }
   else if (1U == NRF_CCM->EVENTS_ERROR)
   {
      /* There has been an error. */

      /* Clear event. */
      NRF_CCM->EVENTS_ERROR = 0U;
   }
}

As the code to shortcut the end of keystream generation to encryption/ decryption is commented out, only the keystream is generated. If a break point is put on the end of keystream event in the interrupt it can be seen that the scratchpad has overflowed.


Any ideas ?

Related