OTA update, signature verification failing

Hey everyone,
I’m using STM32F767, following the OTA demo and implemented the required ota_pal.h functions.
The process of downloading the file is working good, but I have a problem with the signature verification. (sha256 ECDSA)
When using a quite small ‘test file’ for the update (~40KB), the verification works as expected.
But when using the new image file (~650KB), the verification fails.

Specifically in the function prvVerifySignature in iot_crypto.c, mbedtls_pk_verify fail with the error MBEDTLS_ERR_ECP_VERIFY_FAILED -0x4E00

static BaseType_t prvVerifySignature(char *pcSignerCertificate,
                                     size_t xSignerCertificateLength,
                                     BaseType_t xHashAlgorithm,
                                     uint8_t *pucHash,
                                     size_t xHashLength,
                                     uint8_t *pucSignature,
                                     size_t xSignatureLength)
{
    BaseType_t xResult = pdTRUE;
    mbedtls_x509_crt xCertCtx;
    mbedtls_md_type_t xMbedHashAlg = MBEDTLS_MD_SHA256;

    CRYPTO_PRINT(("prvVerifySignature"));

    memset(&xCertCtx, 0, sizeof(mbedtls_x509_crt));

    /*
     * Map the hash algorithm
     */
    if (cryptoHASH_ALGORITHM_SHA1 == xHashAlgorithm)
    {
        xMbedHashAlg = MBEDTLS_MD_SHA1;
    }

    /*
     * Decode and create a certificate context
     */
    CRYPTO_PRINT(("calling mbedtls_x509_crt_init"));
    mbedtls_x509_crt_init(&xCertCtx);
    CRYPTO_PRINT(("calling mbedtls_x509_crt_parse"));

    if (0 != mbedtls_x509_crt_parse(
                 &xCertCtx, (const unsigned char *)pcSignerCertificate, xSignerCertificateLength))
    {
        CRYPTO_PRINT(("mbedtls_x509_crt_parse FAILED"));
        xResult = pdFALSE;
    }

    /*
     * Verify the signature using the public key from the decoded certificate
     */
    if (pdTRUE == xResult)
    {
        CRYPTO_PRINT(("calling mbedtls_pk_verify"));

        if (0 != mbedtls_pk_verify(
                     &xCertCtx.pk,
                     xMbedHashAlg,
                     pucHash,
                     xHashLength,
                     pucSignature,
                     xSignatureLength))
        {
            CRYPTO_PRINT(("mbedtls_pk_verify FAILED"));
            xResult = pdFALSE;
        }
    }

    /*
     * Clean-up
     */
    CRYPTO_PRINT(("calling mbedtls_x509_crt_free"));

    mbedtls_x509_crt_free(&xCertCtx);

    return xResult;
}

The call to prvVerifySignature comes from CRYPTO_SignatureVerificationFinal:

BaseType_t CRYPTO_SignatureVerificationFinal(void *pvContext,
                                             char *pcSignerCertificate,
                                             size_t xSignerCertificateLength,
                                             uint8_t *pucSignature,
                                             size_t xSignatureLength)
{
    CRYPTO_PRINT(("CRYPTO_SignatureVerificationFinal."));
    BaseType_t xResult = pdFALSE;

    if (pvContext != NULL)
    {
        SignatureVerificationStatePtr_t pxCtx = (SignatureVerificationStatePtr_t)pvContext; /*lint !e9087 Allow casting void* to other types. */
        uint8_t ucSHA1or256[cryptoSHA256_DIGEST_BYTES];                                     /* Reserve enough space for the larger of SHA1 or SHA256 results. */
        uint8_t *pucHash = NULL;
        size_t xHashLength = 0;

        if ((pcSignerCertificate != NULL) &&
            (pucSignature != NULL) &&
            (xSignerCertificateLength > 0UL) &&
            (xSignatureLength > 0UL))
        {
            /*
             * Finish the hash
             */
            if (cryptoHASH_ALGORITHM_SHA1 == pxCtx->xHashAlgorithm)
            {
                CRYPTO_PRINT(("CRYPTO_SignatureVerificationFinal: SHA1."));
                (void)mbedtls_sha1_finish_ret(&pxCtx->xSHA1Context, ucSHA1or256);
                pucHash = ucSHA1or256;
                xHashLength = cryptoSHA1_DIGEST_BYTES;
            }
            else
            {
                CRYPTO_PRINT(("CRYPTO_SignatureVerificationFinal: SHA256."));
                if (0 != mbedtls_sha256_finish_ret(&pxCtx->xSHA256Context, ucSHA1or256))
                {
                    CRYPTO_PRINT(("mbedtls_sha256_finish_ret FAILED"));
                }
                pucHash = ucSHA1or256;
                xHashLength = cryptoSHA256_DIGEST_BYTES;
            }

            /*
             * Verify the signature
             */
            CRYPTO_PRINT(("Verifying signature..."));
            xResult = prvVerifySignature(pcSignerCertificate,
                                         xSignerCertificateLength,
                                         pxCtx->xHashAlgorithm,
                                         pucHash,
                                         xHashLength,
                                         pucSignature,
                                         xSignatureLength);
        }
        else
        {
            /* Allow function to be called with only the context pointer for cleanup after a failure. */
            CRYPTO_PRINT(("CRYPTO_SignatureVerificationFinal: Invalid parameters."));
        }

        /*
         * Clean-up
         */
        CRYPTO_PRINT(("CRYPTO_SignatureVerificationFinal: Cleaning up."));
        vPortFree(pxCtx);
    }
    CRYPTO_PRINT(("CRYPTO_SignatureVerificationFinal finished."));

    return xResult;
}

Any ideas to understand what can cause this will help.
Thank you.

Is it possible that you are running out of memory somewhere in the TLS stack? Can you enable mbedTLS debug logs?

How can I enable the debug logs?
MBEDTLS_DEBUG_C already defined and a call to mbedtls_debug_set_threshold(4) was added in tlsSetup function (that called from TLS_FreeRTOS_Connect).
But still I can’t see nothing that relates to mbedTLS in the terminal log.

Does this help - Debugging TLS sessions — Mbed TLS documentation

Running into same issue… What fixed the problem?

Do you have mbedTLS debug logs?

Yes, It doesn’t output any errors when doing the verification…

My 580 KB image fails verification, If I trim the bin file to e.g. 546KB, verification works.

MBEDTLS_ERR_ECP_VERIFY_FAILED happens in step 8 inside the ecdsa_verify_restartable function. Step 8 calls mbedtls_mpi_cmp_mpi where it return -1 when the following is validated in the bottom of the function

if( X->p[i - 1] < Y->p[i - 1] ) return( -X->s );

My debug output, which I’m not even sure is output based on the verification process but rather on background communication with cloud

[INFO] [OtaConfig] [xValidateImageSignature:265] Started sig-sha256-ecdsa signature verification, file: /

0x20000978: in_left: zu, nb_want: zu

0x20000978: => write

0x20000978: => write record

0x20000978: => encrypt buf

0x20000978: dumping 'before encrypt: output payload' (2 bytes)

0x20000978: 0000:  c0 00                                            ..

0x20000978: dumping 'CID' (0 bytes)

0x20000978: dumping 'IV used (internal)' (12 bytes)

0x20000978: 0000:  1b 68 2b 2e 00 00 00 00 00 00 02 7f              .h+.........

0x20000978: dumping 'IV used (transmitted)' (8 bytes)

0x20000978: 0000:  00 00 00 00 00 00 02 7f                          ........

0x20000978: dumping 'additional data used for AEAD' (13 bytes)

0x20000978: 0000:  00 00 00 00 00 00 02 7f 17 03 03 00 02           .............

0x20000978: before encrypt: msglen = zu, including 0 bytes of padding

0x20000978: dumping 'after encrypt: tag' (16 bytes)

0x20000978: 0000:  7c 20 f4 3d e0 b7 27 69 d5 69 b5 f4 68 90 9d 29  | .=..'i.i..h..)

0x20000978: <= encrypt buf

0x20000978: output record: msgtype = 23, version = [3:3], msglen = zu

0x20000978: dumping 'output record sent to network' (31 bytes)

0x20000978: 0000:  17 03 03 00 1a 00 00 00 00 00 00 02 7f e9 61 7c  ..............a|

0x20000978: 0010:  20 f4 3d e0 b7 27 69 d5 69 b5 f4 68 90 9d 29      .=..'i.i..h..)

0x20000978: => flush output

0x20000978: message length: zu, out_left: zu

0x20000978: ssl->f_send() returned 31 (-0xffffffe1)

0x20000978: <= flush output

0x20000978: <= write record

0x20000978: <= write

[INFO] [OtaTask] [OtaThread:337] Received: 290   Queued: 290   Processed: 289   Dropped: 0
0x20000978: => read

0x20000978: => read record

0x20000978: => fetch input

0x20000978: in_left: zu, nb_want: zu

0x20000978: in_left: zu, nb_want: zu

0x20000978: ssl->f_recv(_timeout)() returned 5 (-0xfffffffb)

0x20000978: <= fetch input

0x20000978: dumping 'input record header' (5 bytes)

0x20000978: 0000:  17 03 03 00 1a                                   .....

0x20000978: input record: msgtype = 23, version = [0x303], msglen = zu

0x20000978: => fetch input

0x20000978: in_left: zu, nb_want: zu

0x20000978: in_left: zu, nb_want: zu

0x20000978: ssl->f_recv(_timeout)() returned 26 (-0xffffffe6)

0x20000978: <= fetch input

0x20000978: dumping 'input record from network' (31 bytes)

0x20000978: 0000:  17 03 03 00 1a 00 00 00 00 00 00 01 b1 c1 bf 7c  ...............|

0x20000978: 0010:  24 04 43 56 15 80 10 36 4a d7 85 33 51 0b 57     $.CV...6J..3Q.W

0x20000978: => decrypt buf

0x20000978: dumping 'additional data used for AEAD' (13 bytes)

0x20000978: 0000:  00 00 00 00 00 00 01 b1 17 03 03 00 02           .............

0x20000978: dumping 'IV used' (12 bytes)

0x20000978: 0000:  13 2a 4f 42 00 00 00 00 00 00 01 b1              .*OB........

0x20000978: dumping 'TAG used' (16 bytes)

0x20000978: 0000:  7c 24 04 43 56 15 80 10 36 4a d7 85 33 51 0b 57  |$.CV...6J..3Q.W

0x20000978: <= decrypt buf

0x20000978: dumping 'input payload after decrypt' (2 bytes)

0x20000978: 0000:  d0 00                                            ..

0x20000978: <= read record

0x20000978: <= read

[ERROR] [OtaPal] [otaPal_CloseFile:697] Failed to pass sig-sha256-ecdsa signature verification: 3808428032.

One possibility is that the mbedTLS is running out of memory (though I’d expect that to show up in logs). Which memory scheme are you using for mbedTLS: Alternative ways of allocating memory in Mbed TLS — Mbed TLS documentation? If heap, you can map them to FreeRTOS heap and enable malloc failed hook to catch malloc failure.

Other thing to check is that the complete image is received and written correctly. Can you read the image back from flash and check that it is same as the one sent from the cloud?