AzureIoTResult_t AzureIoTJWS_ManifestAuthenticate()

in ports/mbedTLS/azure_iot_jws_mbedtls.c [700:886]


AzureIoTResult_t AzureIoTJWS_ManifestAuthenticate( const uint8_t * pucManifest,
                                                   uint32_t ulManifestLength,
                                                   uint8_t * pucJWS,
                                                   uint32_t ulJWSLength,
                                                   AzureIoTJWS_RootKey_t * xADURootKeys,
                                                   uint32_t ulADURootKeysLength,
                                                   uint8_t * pucScratchBuffer,
                                                   uint32_t ulScratchBufferLength )
{
    az_result xCoreResult;
    AzureIoTResult_t xResult;
    AzureIoTJSONReader_t xJSONReader;
    prvJWSValidationContext_t xManifestContext = { 0 };
    int32_t lRootKeyIndex;

    /* Break up scratch buffer for reusable and persistant sections */
    uint8_t * ucPersistentScratchSpaceHead = pucScratchBuffer;
    uint8_t * ucReusableScratchSpaceRoot = ucPersistentScratchSpaceHead + azureiotjwsJWS_HEADER_SIZE + azureiotjwsJWK_PAYLOAD_SIZE;
    uint8_t * ucReusableScratchSpaceHead = ucReusableScratchSpaceRoot;

    /*------------------- Parse and Decode the JWS Header ------------------------*/

    xResult = prvSplitJWS( pucJWS, ulJWSLength,
                           &xManifestContext.pucBase64EncodedHeader, &xManifestContext.ulBase64EncodedHeaderLength,
                           &xManifestContext.pucBase64EncodedPayload, &xManifestContext.ulBase64EncodedPayloadLength,
                           &xManifestContext.pucBase64EncodedSignature, &xManifestContext.ulBase64SignatureLength );

    if( xResult != eAzureIoTSuccess )
    {
        AZLogError( ( "[JWS] prvSplitJWS failed" ) );
        return xResult;
    }

    xManifestContext.ucJWSHeader = ucPersistentScratchSpaceHead;
    ucPersistentScratchSpaceHead += azureiotjwsJWS_HEADER_SIZE;
    xCoreResult = az_base64_url_decode( az_span_create( xManifestContext.ucJWSHeader, azureiotjwsJWS_HEADER_SIZE ),
                                        az_span_create( xManifestContext.pucBase64EncodedHeader, xManifestContext.ulBase64EncodedHeaderLength ),
                                        &xManifestContext.outJWSHeaderLength );

    if( az_result_failed( xCoreResult ) )
    {
        AZLogError( ( "[JWS] az_base64_url_decode failed: result 0x%08x", ( uint16_t ) xCoreResult ) );

        if( xCoreResult == AZ_ERROR_NOT_ENOUGH_SPACE )
        {
            AZLogError( ( "[JWS] Decode buffer was too small: %i bytes", azureiotjwsJWS_HEADER_SIZE ) );
        }

        return eAzureIoTErrorFailed;
    }

    /*------------------- Parse SJWK JSON Payload ------------------------*/

    /* The "sjwk" is the signed signing public key */
    AzureIoTJSONReader_Init( &xJSONReader, xManifestContext.ucJWSHeader, xManifestContext.outJWSHeaderLength );

    if( prvFindSJWKValue( &xJSONReader, &xManifestContext.xJWKManifestSpan ) != eAzureIoTSuccess )
    {
        AZLogError( ( "Error finding sjwk value in payload" ) );
        return eAzureIoTErrorFailed;
    }

    /*------------------- Split JWK and Base64 Decode the JWK Payload ------------------------*/

    xResult = prvSplitJWS( az_span_ptr( xManifestContext.xJWKManifestSpan ), az_span_size( xManifestContext.xJWKManifestSpan ),
                           &xManifestContext.pucJWKBase64EncodedHeader, &xManifestContext.ulJWKBase64EncodedHeaderLength,
                           &xManifestContext.pucJWKBase64EncodedPayload, &xManifestContext.ulJWKBase64EncodedPayloadLength,
                           &xManifestContext.pucJWKBase64EncodedSignature, &xManifestContext.ulJWKBase64EncodedSignatureLength );

    if( xResult != eAzureIoTSuccess )
    {
        AZLogError( ( "[JWS] prvSplitJWS failed" ) );
        return xResult;
    }

    xManifestContext.ucJWKHeader = ucReusableScratchSpaceHead;
    ucReusableScratchSpaceHead += azureiotjwsJWK_HEADER_SIZE;
    /* Needs to be persisted so we can parse the signing key N and E later */
    xManifestContext.ucJWKPayload = ucPersistentScratchSpaceHead;
    ucPersistentScratchSpaceHead += azureiotjwsJWK_PAYLOAD_SIZE;
    xManifestContext.ucJWKSignature = ucReusableScratchSpaceHead;
    ucReusableScratchSpaceHead += azureiotjwsSIGNATURE_SIZE;

    prvBase64DecodeJWK( &xManifestContext );

    /*------------------- Parse root key id ------------------------*/

    xResult = prvValidateRootKey( &xManifestContext, xADURootKeys, ulADURootKeysLength, &lRootKeyIndex );

    if( xResult != eAzureIoTSuccess )
    {
        AZLogError( ( "[JWS] prvValidateRootKey failed" ) );
        return xResult;
    }

    /*------------------- Parse necessary pieces for signing key ------------------------*/

    AzureIoTJSONReader_Init( &xJSONReader, xManifestContext.ucJWKPayload, xManifestContext.outJWKPayloadLength );

    if( prvFindKeyParts( &xJSONReader, &xManifestContext.xBase64EncodedNSpan, &xManifestContext.xBase64EncodedESpan, &xManifestContext.xAlgSpan ) != eAzureIoTSuccess )
    {
        AZLogError( ( "Could not find parts for the signing key" ) );
        return eAzureIoTErrorFailed;
    }

    /*------------------- Verify the signature ------------------------*/

    xManifestContext.ucScratchCalculationBuffer = ucReusableScratchSpaceHead;
    ucReusableScratchSpaceHead += azureiotjwsSHA_CALCULATION_SCRATCH_SIZE;
    xResult = prvJWS_RS256Verify( xManifestContext.pucJWKBase64EncodedHeader, xManifestContext.ulJWKBase64EncodedHeaderLength + xManifestContext.ulJWKBase64EncodedPayloadLength + 1,
                                  xManifestContext.ucJWKSignature, xManifestContext.outJWKSignatureLength,
                                  ( uint8_t * ) xADURootKeys[ lRootKeyIndex ].pucRootKeyN, xADURootKeys[ lRootKeyIndex ].ulRootKeyNLength,
                                  ( uint8_t * ) xADURootKeys[ lRootKeyIndex ].pucRootKeyExponent, xADURootKeys[ lRootKeyIndex ].ulRootKeyExponentLength,
                                  xManifestContext.ucScratchCalculationBuffer, azureiotjwsSHA_CALCULATION_SCRATCH_SIZE );

    if( xResult != eAzureIoTSuccess )
    {
        AZLogError( ( "[JWS] prvJWS_RS256Verify failed" ) );
        return xResult;
    }

    /*------------------- Reuse Buffer Space ------------------------*/

    /* The JWK verification is now done, so we can reuse the buffers which it used. */
    ucReusableScratchSpaceHead = ucReusableScratchSpaceRoot;

    /*------------------- Decode remaining values from JWS ------------------------*/

    xManifestContext.ucJWSPayload = ucReusableScratchSpaceHead;
    ucReusableScratchSpaceHead += azureiotjwsJWS_PAYLOAD_SIZE;
    xManifestContext.ucJWSSignature = ucReusableScratchSpaceHead;
    ucReusableScratchSpaceHead += azureiotjwsSIGNATURE_SIZE;

    xResult = prvBase64DecodeJWSHeaderAndPayload( &xManifestContext );

    if( xResult != eAzureIoTSuccess )
    {
        AZLogError( ( "[JWS] prvBase64DecodeJWSHeaderAndPayload failed" ) );
        return xResult;
    }

    /*------------------- Base64 decode the signing key ------------------------*/

    xManifestContext.ucSigningKeyN = ucReusableScratchSpaceHead;
    ucReusableScratchSpaceHead += azureiotjwsRSA3072_SIZE;
    xManifestContext.ucSigningKeyE = ucReusableScratchSpaceHead;
    ucReusableScratchSpaceHead += azureiotjwsSIGNING_KEY_E_SIZE;

    xResult = prvBase64DecodeSigningKey( &xManifestContext );

    if( xResult != eAzureIoTSuccess )
    {
        AZLogError( ( "[JWS] prvBase64DecodeSigningKey failed" ) );
        return xResult;
    }

    /*------------------- Verify that the signature was signed by signing key ------------------------*/

    if( !az_span_is_content_equal( xManifestContext.xAlgSpan, az_span_create( ( uint8_t * ) jws_alg_rs256, sizeof( jws_alg_rs256 ) - 1 ) ) )
    {
        AZLogError( ( "[JWS] Algorithm not supported | expected %.*s | actual %.*s",
                      sizeof( jws_alg_rs256 ) - 1, jws_alg_rs256,
                      ( int16_t ) az_span_size( xManifestContext.xAlgSpan ), az_span_ptr( xManifestContext.xAlgSpan ) ) );
        return eAzureIoTErrorFailed;
    }

    xResult = prvJWS_RS256Verify( xManifestContext.pucBase64EncodedHeader, xManifestContext.ulBase64EncodedHeaderLength + xManifestContext.ulBase64EncodedPayloadLength + 1,
                                  xManifestContext.ucJWSSignature, xManifestContext.outJWSSignatureLength,
                                  xManifestContext.ucSigningKeyN, xManifestContext.outSigningKeyNLength,
                                  xManifestContext.ucSigningKeyE, xManifestContext.outSigningKeyELength,
                                  xManifestContext.ucScratchCalculationBuffer, azureiotjwsSHA_CALCULATION_SCRATCH_SIZE );

    if( xResult != eAzureIoTSuccess )
    {
        AZLogError( ( "[JWS] Verification of signed manifest SHA failed" ) );
        return xResult;
    }

    /*------------------- Verify that the SHAs match ------------------------*/

    xManifestContext.ucManifestSHACalculation = ucReusableScratchSpaceHead;
    ucReusableScratchSpaceHead += azureiotjwsSHA256_SIZE;
    xManifestContext.ucParsedManifestSha = ucReusableScratchSpaceHead;
    ucReusableScratchSpaceHead += azureiotjwsSHA256_SIZE;

    return prvVerifySHAMatch( &xManifestContext, pucManifest, ulManifestLength );
}