KW45 AES GCM decrypt writes to Tag input buffer on failed decryption, MU lockup after

キャンセル
次の結果を表示 
表示  限定  | 次の代わりに検索 
もしかして: 

KW45 AES GCM decrypt writes to Tag input buffer on failed decryption, MU lockup after

ソリューションへジャンプ
2,405件の閲覧回数
BobMaiKaiVa
Contributor I

KW45Z41083 CPU, MCUXpresso SDK v11.10.0_3148.

I ran across an issue with GCM decryption in my software and I'm able to replicate it with a few changes to the ele_symmetric.c example from the SDK. First, here's the code that reproduces this 100%:

#define ELE_MAX_SUBSYSTEM_WAIT (0xFFFFFFFFu)
#define ELE_SUBSYSTEM          (kType_SSS_Ele200)
#define KEY_ID                 (0u)

/* Variables used by example */
static sscp_context_t sscpContext    = {0};
static sss_sscp_session_t sssSession = {0};
static sss_sscp_key_store_t keyStore = {0};

// Taken mostly from ele_symmetric.c example.
status_t AESGCMBug()
{
   status_t status = kStatus_Fail;

   //-------------------------------------------------------------------------------
   // Initialization code from main() ...
   //-------------------------------------------------------------------------------
   do
   {
      status = ELEMU_mu_wait_for_ready(ELEMUA, ELE_MAX_SUBSYSTEM_WAIT);
      if (status != kStatus_Success)
         break;

      /****************** Start   ***********************/
      status = sscp_mu_init(&sscpContext, (ELEMU_Type*) (uintptr_t) ELEMUA);
      if (status != kStatus_SSCP_Success)
         break;

      /* open session to specific security subsystem */
      status = sss_sscp_open_session(&sssSession, 0u, ELE_SUBSYSTEM, &sscpContext);
      if (status != kStatus_SSS_Success)
         return status;

      /* Init keystore  */
      status = sss_sscp_key_store_init(&keyStore, &sssSession);
      if (status != kStatus_SSS_Success)
         break;
   } while (0);

   //-------------------------------------------------------------------------------
   // Encryption code from test_aes_gcm() ...
   //-------------------------------------------------------------------------------

   /* Test data for AES GCM*/

   /*! @brief 16 bytes key for GCM method. */
   SDK_ALIGN(static const uint8_t s_GcmKey[16], 8u) =
      {  0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
         0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08};

   /*! @brief Plaintext for GCM method. */
   SDK_ALIGN(static const uint8_t s_GcmPlain[], 8u) =
      {  0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26,
         0x9a, 0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, 0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31,
         0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24, 0x49,
         0xa6, 0xb5, 0x25, 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57, 0xba, 0x63, 0x7b, 0x39};

   /*! @brief Expected ciphertext from GCM method. */
   static const uint8_t s_GcmCipherExpected[] =
      { 0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, 0x4b, 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c, 0xe3, 0xaa, 0x21,
        0x2f, 0x2c, 0x02, 0xa4, 0xe0, 0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e, 0x21, 0xd5, 0x14, 0xb2, 0x54,
        0x66, 0x93, 0x1c, 0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05, 0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a, 0xac,
        0x97, 0x3d, 0x58, 0xe0, 0x91 };

   /*! @brief Encrypted ciphertext from GCM method goes here. */
   static uint8_t s_OutBuffer[sizeof(s_GcmCipherExpected)]; // was "s_GcmCipher" in example

   /*! @brief Initialization vector for GCM method. */
   SDK_ALIGN(static const uint8_t s_GcmIv[12], 8u) =
      {  0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
         0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88};

   /*! @brief Additional authenticated data for GCM method. */
   SDK_ALIGN(static const uint8_t s_GcmAad[], 8u) =
      {  0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xfe, 0xed,
         0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xab, 0xad, 0xda, 0xd2};

   /*! @brief Expected tag from GCM method. */
   static const uint8_t s_GcmTagExpected[] =
      { 0x5b, 0xc9, 0x4f, 0xbc, 0x32, 0x21, 0xa5, 0xdb, 0x94, 0xfa, 0xe9, 0x5a, 0xe7, 0x12, 0x1a, 0x47 };

   /*! @brief Encrypted tag from GCM method goes here. */
   static uint8_t s_GcmTag[sizeof(s_GcmTagExpected)];

   sss_sscp_object_t sssKey = { 0 };
   sss_sscp_aead_t ctx_encrypt = { 0 };
   sss_sscp_aead_t ctx_decrypt = { 0 };
   size_t taglen = sizeof(s_GcmTag);

   do
   {
      /* Init key object  */
      status = sss_sscp_key_object_init(&sssKey, &keyStore);
      if (status != kStatus_SSS_Success)
      {
         break;
      }

      /* Allocate keystore handle */
      status = sss_sscp_key_object_allocate_handle(&sssKey, KEY_ID, /* key id */
            kSSS_KeyPart_Default, kSSS_CipherType_AES, 16u, kSSS_KeyProp_CryptoAlgo_AEAD);
      if (status != kStatus_SSS_Success)
      {
         (void) sss_sscp_key_object_free(&sssKey, kSSS_keyObjFree_KeysStoreDefragment);
         break;
      }

      /* Set key into key object*/
      status = sss_sscp_key_store_set_key(&keyStore, &sssKey, s_GcmKey, sizeof(s_GcmKey), (sizeof(s_GcmKey) * 8U),
            kSSS_KeyPart_Default);
      if (status != kStatus_SSS_Success)
      {
         (void) sss_sscp_key_object_free(&sssKey, kSSS_keyObjFree_KeysStoreDefragment);
         break;
      }

      /* Init aead context */
      status = sss_sscp_aead_context_init(&ctx_encrypt, &sssSession, &sssKey, kAlgorithm_SSS_AES_GCM, kMode_SSS_Encrypt);
      if (status != kStatus_SSS_Success)
      {
         (void) sss_sscp_key_object_free(&sssKey, kSSS_keyObjFree_KeysStoreDefragment);
         break;
      }

      /* RUN AES-GCM Encryption */
      status = sss_sscp_aead_one_go(&ctx_encrypt, s_GcmPlain, s_OutBuffer, sizeof(s_GcmPlain), (uint8_t *)(uint32_t)s_GcmIv, sizeof(s_GcmIv),
            s_GcmAad, sizeof(s_GcmAad), s_GcmTag, &taglen);
      if (status != kStatus_SSS_Success)
      {
         (void) sss_sscp_aead_context_free(&ctx_encrypt);
         (void) sss_sscp_key_object_free(&sssKey, kSSS_keyObjFree_KeysStoreDefragment);
         break;
      }
      else
      {
         if (memcmp(s_OutBuffer, s_GcmCipherExpected, sizeof(s_OutBuffer)) == 0
               && memcmp(s_GcmTag, s_GcmTagExpected, sizeof(s_GcmTag)) == 0)
         {
            //PRINTF("AES-GCM crypto encryption success and output matches expected result.\r\n\r\n");
            status = kStatus_Success;
         }
         else
         {
            status = kStatus_Fail;
            break;
         }
      }

      //-------------------------------------------------------------------------------
      // My additions to test GCM decryption ...
      //-------------------------------------------------------------------------------

      /* Init a 2nd context for decryptions */
      status = sss_sscp_aead_context_init(&ctx_decrypt, &sssSession, &sssKey, kAlgorithm_SSS_AES_GCM, kMode_SSS_Decrypt);
      if (status != kStatus_SSS_Success)
      {
         (void) sss_sscp_key_object_free(&sssKey, kSSS_keyObjFree_KeysStoreDefragment);
         break;
      }

      // Flip one bit in byte 0 of the tag data to force a decryption failure.
      static const uint8_t s_GcmTagInvalid[] =
         { 0xdb, 0xc9, 0x4f, 0xbc, 0x32, 0x21, 0xa5, 0xdb, 0x94, 0xfa, 0xe9, 0x5a, 0xe7, 0x12, 0x1a, 0x47 };

      /* RUN AES-GCM Decryption */
      status = sss_sscp_aead_one_go(&ctx_decrypt, s_GcmCipherExpected, s_OutBuffer, sizeof(s_GcmCipherExpected), (uint8_t *)(uint32_t)s_GcmIv, sizeof(s_GcmIv),
            s_GcmAad, sizeof(s_GcmAad), (uint8_t *)(uint32_t)s_GcmTagInvalid, &taglen);
      if (status == kStatus_SSS_Success) // previous call should have reported an error
      {
         (void) sss_sscp_aead_context_free(&ctx_decrypt);
         (void) sss_sscp_key_object_free(&sssKey, kSSS_keyObjFree_KeysStoreDefragment);
         break;
      }

      //-------------------------------------------------------------------------------
      // Remainder of test_aes_gcm() ...
      //-------------------------------------------------------------------------------

      // This hangs at "while ((mu->TSR & mask) == 0u)" in ELEMU_mu_hal_send_data() with call stack:
      //    ELEMU_mu_hal_send_data() at fsl_elemu.c:48 0x44bfc
      //    ELEMU_mu_send_message() at fsl_elemu.c:117 0x3254a
      //    MU_SendMsg() at fsl_sscp_mu.c:54 0x30fc8
      //    sscp_mu_invoke_command() at fsl_sscp_mu.c:223 0x31400
      //    sss_sscp_aead_context_free() at fsl_sss_sscp.c:455 0x31964
      //    AESGCMBug() at HSM.c:267 0x3f75e
      status = sss_sscp_aead_context_free(&ctx_decrypt);

      // The rest of test_aes_gcm() is deleted / not needed for this example.

   } while (0);

   return status;
}

95% of this is directly from the SDK example ele_symmetric.c. The example doesn't show how to decrypt and verify the tag is correct, so I added that in the "My additions to test GCM decryption ..." block.

Observations:

1) If I set the data in s_GcmTagInvalid[] to match exactly the data in s_GcmTagExpected[], the decrypt and tag checking inside EdgeLock work perfectly as expected. The sss_sscp_aead_one_go() call for decryption succeeds and all is well.

2) If I run the code as shown above (I purposeIy corrupted the tag data) I get some strange results... sss_sscp_aead_one_go() eventually calls sscp_mu_invoke_command() to send a kSSCP_CMD_SSS_AeadOneGo (commandId == 0x29) command to EdgeLock. A response is received but the check at "if (muReplyHeader->command != commandId)" fails due to muReplyHeader->command being 0x1D. I don't see an enum value for 0x1D so no idea what that means.

After this, the MU interface to EdgeLock is completely hosed and the next attempt at an MU command (sss_sscp_aead_context_free() in my example) causes a watchdog.

3) If I drop the "const" from "static const uint8_t s_GcmTagInvalid[]", the call to sss_sscp_aead_one_go() does what it's supposed to do... it sends the kSSCP_CMD_SSS_AeadOneGo command, receives a proper response with muReplyHeader->command == kSSCP_CMD_SSS_AeadOneGo, and returns an kStatus_SSS_Fail as expected to report the failed decryption / tag check.

HOWEVER, I noticed that after this, my s_GcmTagInvalid[] array is now all zeros! This should not happen. The Tag buffer is an input in GCM decryption, it should not get corrupted as a result of a failed decryption!

 

I assume the issue/lockup reported in #2 is caused by EdgeLock trying to write back to my Tag array but the Tag array is read-only in flash.

A workaround could be to copy the Tag data for decryptions to a separate RAM buffer and pass that to sss_sscp_aead_one_go() but I hate that solution because it wastes CPU cycles unnecessarily and forces me to allocate a separate buffer for decryption Tag data. I'd have to dynamically allocate that buffer based on application needs for each decryption, or artificially impose some limit on max Tag size. I really don't want to do that.

 

0 件の賞賛
返信
1 解決策
1,910件の閲覧回数
luis_maravilla
NXP Employee
NXP Employee

When a security subsystem fails accordingly to reading the wrong information in a tag, this is the fail that is detected on sss_sscp_aead_one_go(), trying the process of modifying again the tag is out of the scope for this test example, as this example have the function to decrypt already and fails if the system discover something different to CipherExpected or Tag Expected,  detecting the Secure subsystem fail is part of detecting the 0x1D as the wrong answer.

Managing this behavior, would depend on the top application, and this is something you would need to implement.

元の投稿で解決策を見る

0 件の賞賛
返信
6 返答(返信)
2,144件の閲覧回数
BobMaiKaiVa
Contributor I

>> Don’t need to create another context, you need to use the same data for Decryption

I disagree. The last parameter to sss_sscp_aead_context_init() is either kMode_SSS_Encrypt or kMode_SSS_Decrypt depending on what you plan to do with that context structure. If I used one context struct for both encryption and decryption, I'd need to call sss_sscp_aead_context_init() again on every encryption or decryption which is wasteful. As long as the key isn't changing I should be able to initialize mt context structs one time as startup and leave them alone after that for optimal performance.

>> Changes to Decrypt ...

I'm not sure you understand what I'm trying to do here. I'm testing an error mode where I simulate receiving a ciphertext, aad, and a tag that has a single bit error. I want to make sure sss_sscp_aead_one_go() detects this decryption error and fails accordingly.

My real tests (not shown above) will then try the decryption again using valid tag data and make sure sss_sscp_aead_one_go() succeeds. In my observations 2) and 3) in the original post, I observed several issues I'd like NXP to comment on. Locking up and watchdogging the system would seem to be something NXP would want to try to avoid.

Can you please download and run my example source code and verify you're seeing the same results I was getting?

0 件の賞賛
返信
1,911件の閲覧回数
luis_maravilla
NXP Employee
NXP Employee

When a security subsystem fails accordingly to reading the wrong information in a tag, this is the fail that is detected on sss_sscp_aead_one_go(), trying the process of modifying again the tag is out of the scope for this test example, as this example have the function to decrypt already and fails if the system discover something different to CipherExpected or Tag Expected,  detecting the Secure subsystem fail is part of detecting the 0x1D as the wrong answer.

Managing this behavior, would depend on the top application, and this is something you would need to implement.

0 件の賞賛
返信
2,208件の閲覧回数
BobMaiKaiVa
Contributor I

I received an email from NXP asking to mark this accepted but Luis is still debugging this as far as I know, so this is still unresolved.

0 件の賞賛
返信
2,170件の閲覧回数
luis_maravilla
NXP Employee
NXP Employee

Hi Bob

 

Don’t need to create another context, you need to use the same data for Decryption using

[status = sss_sscp_aead_one_go] and changing the entries.

 

Changes to Decrypt

 

Using the same data the iv and the add dont need to change. The s_GcmCipherExpected will replace the slot where s_GcmPlain is in the function this is your inputData. The s_GcmCipher should now be the empty buffer where your output will be written, so this should be s_GcmPlain this is your outputData. Finally, use the s_GcmTagExpected data for the actual s_GcmTag this is your tagData.

 

Example: status = sss_sscp_aead_one_go(&ctx, inputData, outputData, 60, ivData, 12,aadData, 20, tagData, &taglen);

 

For more detailed documentation about your topic, I recommend checking the KW45 Security Reference Manual. Chapter 7.6 of the security reference manual provides details of the ELE commands.

This file is under Secure Files. Secure Access Rights | NXP Semiconductors 

Follow the process in order to download the File in the KW45 main page-Documents. Also, I would recommend checking the Secure Access Rights FAQs | NXP Semiconductors

0 件の賞賛
返信
2,320件の閲覧回数
BobMaiKaiVa
Contributor I

This is a custom board but I'm guessing you'll be able to reproduce it on the EVK board.

SDK version SDK_2.x_KW45Z41083xxxA v2.16.000

0 件の賞賛
返信
2,343件の閲覧回数
luis_maravilla
NXP Employee
NXP Employee

Hi Bob,

Could you please help us confirming if you are using the KW45B41Z-EVK or a custom board? and if you can confirm what SDK version are you using(2.16.100 is the latest) as you mentioned that you're working on MCUXpresso IDE v11.10.0_3148. This is for my attention to debug the code inside the SDK using the code you post.

 

If you have any more information will be helpful thank you.

Regards,
Luis

0 件の賞賛
返信