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 );
}