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 ?