diff --git a/demos/src/aws_iot_demo_onboarding.c b/demos/src/aws_iot_demo_onboarding.c index 5bb2f4f521..d1393b080e 100644 --- a/demos/src/aws_iot_demo_onboarding.c +++ b/demos/src/aws_iot_demo_onboarding.c @@ -91,14 +91,17 @@ #define NUM_OF_PROVISIONING_PARAMS 1u /** - * @brief Type for the callback context for the #AwsIotOnboarding_GetDeviceCredentials API. - * It will be used for storing the received Certificate ID string that will be provided in the callback. + * @brief Type for the context parameter for the #AwsIotOnboarding_DeviceCredentialsCallbackInfo_t callback. + * It will be used for storing the received Certificate ID and the ownership token data received from the server through + * the callback, so that can be used for provisioning the demo application. */ -typedef struct _demoProvisionDeviceCallbackContext +typedef struct _demoDeviceCredentialsCallbackContext { char * pCertificateIdBuffer; size_t certificateIdLength; -} _demoProvisionDeviceCallbackContext_t; + char * pCertificateOwnershipToken; + size_t tokenLength; +} _demoDeviceCredentialsCallbackContext_t; /*-----------------------------------------------------------*/ @@ -125,7 +128,7 @@ static void _printRejectedResponse( const AwsIotOnboardingRejectedResponse_t * p /** * @brief Callback for displaying the credentials sent by the server (if server is successful), and - * copying the Certificate ID data obtained from the server issued with the new device credentials. + * copying the Certificate ID and ownership token data obtained from the server issued with the new device credentials. * * @param[in] contextParam The context of the callback containing buffers to copy the credentials to. * @param[in] pResponseInfo The device credentials information obtained from the server that will be copied into the @@ -134,35 +137,56 @@ static void _printRejectedResponse( const AwsIotOnboardingRejectedResponse_t * p static void _demoDeviceCredentialsCallback( void * contextParam, const AwsIotOnboardingGetDeviceCredentialsResponse_t * pResponseInfo ) { - _demoProvisionDeviceCallbackContext_t * certificateIdContext = ( _demoProvisionDeviceCallbackContext_t * ) contextParam; + _demoDeviceCredentialsCallbackContext_t * certificateIdTokenContext = + ( _demoDeviceCredentialsCallbackContext_t * ) contextParam; IotLogInfo( "Received StatusCode={%d}", pResponseInfo->statusCode ); if( pResponseInfo->statusCode == AWS_IOT_ONBOARDING_SERVER_STATUS_ACCEPTED ) { /* Allocate buffer space for storing the certificate ID obtained from the server. */ - certificateIdContext->pCertificateIdBuffer = Iot_DefaultMalloc( pResponseInfo->u.acceptedResponse.certificateIdLength + 1 ); + certificateIdTokenContext->pCertificateIdBuffer = + Iot_DefaultMalloc( pResponseInfo->u.acceptedResponse.certificateIdLength + 1 ); /* Copy the certificate ID into the buffer. */ - if( certificateIdContext->pCertificateIdBuffer != NULL ) + if( certificateIdTokenContext->pCertificateIdBuffer != NULL ) { /* Copy the size of the Certificate ID string. */ - certificateIdContext->certificateIdLength = pResponseInfo->u.acceptedResponse.certificateIdLength; + certificateIdTokenContext->certificateIdLength = pResponseInfo->u.acceptedResponse.certificateIdLength; - memcpy( certificateIdContext->pCertificateIdBuffer, + memcpy( certificateIdTokenContext->pCertificateIdBuffer, pResponseInfo->u.acceptedResponse.pCertificateId, pResponseInfo->u.acceptedResponse.certificateIdLength ); /* Add a NULL terminator to the buffer (to treat the buffer as a string!) */ - *( certificateIdContext->pCertificateIdBuffer + pResponseInfo->u.acceptedResponse.certificateIdLength ) = '\0'; + *( certificateIdTokenContext->pCertificateIdBuffer + pResponseInfo->u.acceptedResponse.certificateIdLength ) = '\0'; } - /* Print the certificate Pem and private key data received. This is ONLY for the demo purpose, STORE THE + /* Allocate buffer space for storing the ownership token string obtained from the server. */ + certificateIdTokenContext->pCertificateOwnershipToken = + Iot_DefaultMalloc( pResponseInfo->u.acceptedResponse.ownershipTokenLength + 1 ); + + /* Copy the ownership token into the buffer. */ + if( certificateIdTokenContext->pCertificateOwnershipToken != NULL ) + { + /* Copy the size of the ownership token string. */ + certificateIdTokenContext->tokenLength = pResponseInfo->u.acceptedResponse.ownershipTokenLength; + + memcpy( certificateIdTokenContext->pCertificateOwnershipToken, + pResponseInfo->u.acceptedResponse.pCertificateOwnershipToken, + pResponseInfo->u.acceptedResponse.ownershipTokenLength ); + /* Add a NULL terminator to the buffer (to treat the buffer as a string!) */ + *( certificateIdTokenContext->pCertificateOwnershipToken + pResponseInfo->u.acceptedResponse.ownershipTokenLength ) = '\0'; + } + + /* Print the received credentials information. This is ONLY for the demonstration purpose, STORE THE * PRIVATE KEY SECURELY! */ - IotLogInfo( "\n Certificate PEM = %.*s \n Certificate ID = %.*s \n DREADED PRIVATE KEY = %.*s \n", + IotLogInfo( "\n Certificate PEM = %.*s\n Certificate ID = %.*s\n Ownership Token = %.*s\n DREADED PRIVATE KEY = %.*s\n", pResponseInfo->u.acceptedResponse.deviceCertificateLength, pResponseInfo->u.acceptedResponse.pDeviceCertificate, pResponseInfo->u.acceptedResponse.certificateIdLength, pResponseInfo->u.acceptedResponse.pCertificateId, + pResponseInfo->u.acceptedResponse.ownershipTokenLength, + pResponseInfo->u.acceptedResponse.pCertificateOwnershipToken, pResponseInfo->u.acceptedResponse.privateKeyLength, pResponseInfo->u.acceptedResponse.pPrivateKey ); } @@ -190,6 +214,13 @@ static void _demoOnboardDeviceCallback( void * contextParam, if( pResponseInfo->statusCode == AWS_IOT_ONBOARDING_SERVER_STATUS_ACCEPTED ) { + if( pResponseInfo->u.acceptedResponse.pClientId != NULL ) + { + IotLogInfo( "ClientID = %.*s", + pResponseInfo->u.acceptedResponse.clientIdLength, + pResponseInfo->u.acceptedResponse.pClientId ); + } + if( pResponseInfo->u.acceptedResponse.pThingName != NULL ) { IotLogInfo( "ThingName = %.*s", @@ -379,10 +410,12 @@ int RunOnboardingDemo( bool awsIotMqttMode, /* Represents memory that will be allocated to store the Certificate ID that will be provided by the credential * requesting API through the callback. */ - _demoProvisionDeviceCallbackContext_t newCertificateIdDataContext; + _demoDeviceCredentialsCallbackContext_t newCertificateDataContext; - newCertificateIdDataContext.pCertificateIdBuffer = NULL; - newCertificateIdDataContext.certificateIdLength = 0; + newCertificateDataContext.pCertificateIdBuffer = NULL; + newCertificateDataContext.certificateIdLength = 0; + newCertificateDataContext.pCertificateOwnershipToken = NULL; + newCertificateDataContext.tokenLength = 0; /* Request data for onboarding the demo application. */ AwsIotOnboardingOnboardDeviceRequestInfo_t requestInfo; @@ -445,7 +478,7 @@ int RunOnboardingDemo( bool awsIotMqttMode, connectionEstablished = true; /* Set the certificate ID pointer as context parameter to the credentials response processing callback. */ - deviceCredentialsCallback.userParam = &newCertificateIdDataContext; + deviceCredentialsCallback.userParam = &newCertificateDataContext; /* Set the callback function for handling device credentials that the server will send. */ deviceCredentialsCallback.function = _demoDeviceCredentialsCallback; @@ -463,9 +496,10 @@ int RunOnboardingDemo( bool awsIotMqttMode, IotLogError( "Request to get new credentials failed, error %s ", AwsIotOnboarding_strerror( requestStatus ) ); } - else if( newCertificateIdDataContext.pCertificateIdBuffer == NULL ) + else if( ( newCertificateDataContext.pCertificateIdBuffer == NULL ) || + ( newCertificateDataContext.pCertificateOwnershipToken == NULL ) ) { - IotLogInfo( "Don 't have the Certificate ID to proceed for onboarding. So exiting...!" ); + IotLogInfo( "Don't have either the Certificate ID OR the Certificate Ownership Token (or both) to proceed with provisioning. So exiting...!" ); } else { @@ -476,8 +510,10 @@ int RunOnboardingDemo( bool awsIotMqttMode, if( status == EXIT_SUCCESS ) { /* Set the parameters for requesting onboarding. */ - requestInfo.pDeviceCertificateId = newCertificateIdDataContext.pCertificateIdBuffer; - requestInfo.deviceCertificateIdLength = newCertificateIdDataContext.certificateIdLength; + requestInfo.pDeviceCertificateId = newCertificateDataContext.pCertificateIdBuffer; + requestInfo.deviceCertificateIdLength = newCertificateDataContext.certificateIdLength; + requestInfo.pCertificateOwnershipToken = newCertificateDataContext.pCertificateOwnershipToken; + requestInfo.ownershipTokenLength = newCertificateDataContext.tokenLength; requestInfo.pTemplateName = PROVISIONING_TEMPLATE_NAME; requestInfo.templateNameLength = sizeof( PROVISIONING_TEMPLATE_NAME ) - 1; requestInfo.pParametersStart = &provisioningParameters; @@ -518,9 +554,15 @@ int RunOnboardingDemo( bool awsIotMqttMode, } /* Release the Certificate ID buffer, if memory was allocated for it. */ - if( newCertificateIdDataContext.pCertificateIdBuffer != NULL ) + if( newCertificateDataContext.pCertificateIdBuffer != NULL ) + { + Iot_DefaultFree( newCertificateDataContext.pCertificateIdBuffer ); + } + + /* Release the ownership token buffer, if memory was allocated for it. */ + if( newCertificateDataContext.pCertificateOwnershipToken != NULL ) { - Iot_DefaultFree( newCertificateIdDataContext.pCertificateIdBuffer ); + Iot_DefaultFree( newCertificateDataContext.pCertificateOwnershipToken ); } return status; diff --git a/libraries/aws/onboarding/include/types/aws_iot_onboarding_types.h b/libraries/aws/onboarding/include/types/aws_iot_onboarding_types.h index 92d176ebd4..9e92566d00 100644 --- a/libraries/aws/onboarding/include/types/aws_iot_onboarding_types.h +++ b/libraries/aws/onboarding/include/types/aws_iot_onboarding_types.h @@ -235,6 +235,17 @@ typedef struct AwsIotOnboardingOnboardDeviceRequestInfo */ size_t deviceCertificateIdLength; + /** + * @brief The ownership token for the certificate being requested for provisioning the device with. + * This token should have been issued by AWS IoT for the same certificate ID being passed. + */ + const char * pCertificateOwnershipToken; + + /** + * @brief The length of the ownership token string. + */ + size_t ownershipTokenLength; + /** * @brief The list of parameter entries to send to the server for onboarding the device. */ @@ -281,8 +292,8 @@ typedef struct AwsIotOnboardingRejectedResponse /** * @ingroup onboarding_datatypes_paramstructs - * @brief Parameter that encapsulates the server response to the callback function for the - * #AwsIotOnboarding_GetDeviceCredentials API. + * @brief Encapsulates the server response data sent for the request to generate new key-pair and + * certificate for the device. * * @paramfor Onboarding server accepted response callback functions * @@ -299,14 +310,17 @@ typedef struct AwsIotOnboardingGetDeviceCredentialsResponse /* Represents the successful/accepted response of device credentials received from the server. */ struct { - const char * pDeviceCertificate; /**< The new certificate for the device.*/ - size_t deviceCertificateLength; /**< The size of the device certificate.*/ - const char * pCertificateId; /**< The certificate ID associated with the new certificate, - * @p pDeviceCertificate.*/ - size_t certificateIdLength; /**< The length of the certificate ID.*/ - const char * pPrivateKey; /**< The private key associated with the new certificate, - * @p pDeviceCertificate.*/ - size_t privateKeyLength; /**< The size of the private key.*/ + const char * pDeviceCertificate; /**< The new certificate for the device.*/ + size_t deviceCertificateLength; /**< The size of the device certificate.*/ + const char * pCertificateId; /**< The certificate ID associated with the new certificate, + * @p pDeviceCertificate.*/ + size_t certificateIdLength; /**< The length of the certificate ID.*/ + const char * pPrivateKey; /**< The private key associated with the new certificate, + * @p pDeviceCertificate.*/ + size_t privateKeyLength; /**< The size of the private key.*/ + const char * pCertificateOwnershipToken; /**< The token that represents ownership of certificate and + * associated private key that the device.*/ + size_t ownershipTokenLength; /**< The size of the ownership token.*/ } acceptedResponse; /* Represents the rejected response information received from the server. */ @@ -348,13 +362,13 @@ typedef struct AwsIotOnboardingGetDeviceCredentialsCallbackInfo /** * @ingroup onboarding_datatypes_paramstructs - * @brief Parameter that encapsulates the server response to the callback function for the - * #AwsIotOnboarding_OnboardDevice API. + * @brief Encpasulates the server response data sent for the request to provision device with an AWS IoT issued + * certificate. * * @paramfor Onboarding server accepted response callback functions * - * The #AwsIotOnboarding_OnboardDevice library API passes this object to a user-provided callback function whenever - * the operation completes with a response from the server. + * The #AwsIotOnboarding_OnboardDevice library API passes this object as a parameter to a user-provided callback + * function whenever the operation completes with a response from the server. */ typedef struct AwsIotOnboardingOnboardDeviceResponse { @@ -372,6 +386,12 @@ typedef struct AwsIotOnboardingOnboardDeviceResponse /**< The length of the Thing resource name. */ size_t thingNameLength; + /**< The client ID used in the connection to the AWS IoT server for provisioning the device.*/ + const char * pClientId; + + /**< The length of the client ID text. */ + size_t clientIdLength; + /**< A list of device configuration data that is received from the server. */ const AwsIotOnboardingResponseDeviceConfigurationEntry_t * pDeviceConfigList; diff --git a/libraries/aws/onboarding/src/aws_iot_onboarding_api.c b/libraries/aws/onboarding/src/aws_iot_onboarding_api.c index d3ed6c9e48..77116b944e 100644 --- a/libraries/aws/onboarding/src/aws_iot_onboarding_api.c +++ b/libraries/aws/onboarding/src/aws_iot_onboarding_api.c @@ -661,6 +661,14 @@ AwsIotOnboardingError_t AwsIotOnboarding_OnboardDevice( IotMqttConnection_t onbo IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_BAD_PARAMETER ); } + if( ( pRequestData->pCertificateOwnershipToken == NULL ) || + ( pRequestData->ownershipTokenLength == 0 ) ) + { + IotLogError( "Invalid certificate ownership token data passed for device onboarding request." ); + + IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_BAD_PARAMETER ); + } + /* Check that the provided template name is valid. */ if( ( pRequestData->pTemplateName == NULL ) || ( pRequestData->templateNameLength == 0 ) || diff --git a/libraries/aws/onboarding/src/aws_iot_onboarding_parser.c b/libraries/aws/onboarding/src/aws_iot_onboarding_parser.c index 54ab5b5572..797ec0deef 100644 --- a/libraries/aws/onboarding/src/aws_iot_onboarding_parser.c +++ b/libraries/aws/onboarding/src/aws_iot_onboarding_parser.c @@ -78,7 +78,7 @@ static AwsIotOnboardingError_t _parseRejectedResponse( IotSerializerDecoderObjec &statusCodeDecoder ) != IOT_SERIALIZER_SUCCESS ) { IotLogError( "Cannot find entry for \"%s\" in response from server of %s operation.", - ONBOARDING_GET_DEVICE_CREDENTIALS_RESPONSE_PAYLOAD_CERTIFICATE_PEM_STRING, + ONBOARDING_REJECTED_RESPONSE_STATUS_CODE_STRING, pOperationName ); IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_BAD_RESPONSE ); } @@ -164,6 +164,7 @@ AwsIotOnboardingError_t _AwsIotOnboarding_ParseDeviceCredentialsResponse( AwsIot IotSerializerDecoderObject_t certificatePemDecoder = IOT_SERIALIZER_DECODER_OBJECT_INITIALIZER; IotSerializerDecoderObject_t certificateIdDecoder = IOT_SERIALIZER_DECODER_OBJECT_INITIALIZER; IotSerializerDecoderObject_t privateKeyDecoder = IOT_SERIALIZER_DECODER_OBJECT_INITIALIZER; + IotSerializerDecoderObject_t ownershipTokenDecoder = IOT_SERIALIZER_DECODER_OBJECT_INITIALIZER; if( _pAwsIotOnboardingDecoder->init( &payloadDecoder, pDeviceCredentialsResponse, @@ -188,6 +189,7 @@ AwsIotOnboardingError_t _AwsIotOnboarding_ParseDeviceCredentialsResponse( AwsIot { case AWS_IOT_ACCEPTED: + /* Look for the certificate PEM data. */ if( _pAwsIotOnboardingDecoder->find( &payloadDecoder, ONBOARDING_GET_DEVICE_CREDENTIALS_RESPONSE_PAYLOAD_CERTIFICATE_PEM_STRING, &certificatePemDecoder ) != IOT_SERIALIZER_SUCCESS ) @@ -208,6 +210,7 @@ AwsIotOnboardingError_t _AwsIotOnboarding_ParseDeviceCredentialsResponse( AwsIot IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_BAD_RESPONSE ); } + /* Look for the certificate ID data. */ if( _pAwsIotOnboardingDecoder->find( &payloadDecoder, ONBOARDING_GET_DEVICE_CREDENTIALS_RESPONSE_PAYLOAD_CERTIFICATE_ID_STRING, &certificateIdDecoder ) != IOT_SERIALIZER_SUCCESS ) @@ -228,6 +231,7 @@ AwsIotOnboardingError_t _AwsIotOnboarding_ParseDeviceCredentialsResponse( AwsIot IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_BAD_RESPONSE ); } + /* Look for the private Key data. */ if( _pAwsIotOnboardingDecoder->find( &payloadDecoder, ONBOARDING_GET_DEVICE_CREDENTIALS_RESPONSE_PAYLOAD_PRIVATE_KEY_STRING, &privateKeyDecoder ) != IOT_SERIALIZER_SUCCESS ) @@ -248,6 +252,27 @@ AwsIotOnboardingError_t _AwsIotOnboarding_ParseDeviceCredentialsResponse( AwsIot IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_BAD_RESPONSE ); } + /* Look for the certificate ownership token data. */ + if( _pAwsIotOnboardingDecoder->find( &payloadDecoder, + ONBOARDING_GET_DEVICE_CREDENTIALS_RESPONSE_PAYLOAD_CERTIFICATE_TOKEN_KEY_STRING, + &ownershipTokenDecoder ) != IOT_SERIALIZER_SUCCESS ) + { + /* Cannot find "certificate ownership token" */ + IotLogError( "Cannot find entry for \"%s\" in response from server of %s operation.", + ONBOARDING_GET_DEVICE_CREDENTIALS_RESPONSE_PAYLOAD_CERTIFICATE_TOKEN_KEY_STRING, + GET_DEVICE_CREDENTIALS_OPERATION_LOG ); + IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_BAD_RESPONSE ); + } + + if( ownershipTokenDecoder.type != IOT_SERIALIZER_SCALAR_TEXT_STRING ) + { + IotLogError( + "Invalid value type of \"%s\" data in server response of %s operation. Expected type is text string.", + ONBOARDING_GET_DEVICE_CREDENTIALS_RESPONSE_PAYLOAD_CERTIFICATE_TOKEN_KEY_STRING, + GET_DEVICE_CREDENTIALS_OPERATION_LOG ); + IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_BAD_RESPONSE ); + } + /* Populate the status code information to represent success response from the server. */ userCallbackParam.statusCode = AWS_IOT_ONBOARDING_SERVER_STATUS_ACCEPTED; @@ -265,6 +290,11 @@ AwsIotOnboardingError_t _AwsIotOnboarding_ParseDeviceCredentialsResponse( AwsIot userCallbackParam.u.acceptedResponse.privateKeyLength = privateKeyDecoder.u.value.u.string.length; + userCallbackParam.u.acceptedResponse.pCertificateOwnershipToken = ( const char * ) + ownershipTokenDecoder.u.value.u.string.pString; + userCallbackParam.u.acceptedResponse.ownershipTokenLength = + ownershipTokenDecoder.u.value.u.string.length; + /* Invoke the user-provided callback with the parsed credentials data . */ userCallbackInfo->getDeviceCredentialsCallback.function( userCallbackInfo->getDeviceCredentialsCallback.userParam, &userCallbackParam ); @@ -319,6 +349,7 @@ AwsIotOnboardingError_t _AwsIotOnboarding_ParseOnboardDeviceResponse( AwsIotStat AwsIotOnboardingResponseDeviceConfigurationEntry_t * pDeviceConfigurationList = NULL; bool configurationListAllocated = false; IotSerializerDecoderObject_t thingNameDecoder = IOT_SERIALIZER_DECODER_OBJECT_INITIALIZER; + IotSerializerDecoderObject_t clientIdDecoder = IOT_SERIALIZER_DECODER_OBJECT_INITIALIZER; if( _pAwsIotOnboardingDecoder->init( &payloadDecoder, pResponsePayload, @@ -365,7 +396,7 @@ AwsIotOnboardingError_t _AwsIotOnboarding_ParseOnboardDeviceResponse( AwsIotStat if( deviceConfigurationDecoder.type != IOT_SERIALIZER_CONTAINER_MAP ) { IotLogError( - "Invalid device configuration data received server response for %s operation. Data is expected to be encoded as map container.", + "Invalid device configuration data received in server response for %s operation. Data is expected to be encoded as map container.", GET_ONBOARD_DEVICE_OPERATION_LOG ); IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_BAD_RESPONSE ); } @@ -420,6 +451,38 @@ AwsIotOnboardingError_t _AwsIotOnboarding_ParseOnboardDeviceResponse( AwsIotStat IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_INTERNAL_FAILURE ); } + /* Validate that both the device configuration entry's key and value data are text strings. */ + if( ( deviceConfigInnerKeyDecoder.type != IOT_SERIALIZER_SCALAR_TEXT_STRING ) || + ( deviceConfigInnerValueDecoder.type != IOT_SERIALIZER_SCALAR_TEXT_STRING ) ) + { + /**The serializer library allocates memory for the iterator. It can + * only be released by iterating to the last element in the map containers and "stepping out" of + * the container + * Thus, we will iterate to the end of the device configuration container to invalidate the + * iterator. */ + size_t nextConfigEntryIndex = configurationListIndex + 1; + + while( nextConfigEntryIndex < numOfDeviceConfigurationEntries ) + { + _pAwsIotOnboardingDecoder->next( deviceConfigIter ); + _pAwsIotOnboardingDecoder->next( deviceConfigIter ); + nextConfigEntryIndex++; + } + + /* Advance to the "end" of the container. */ + _pAwsIotOnboardingDecoder->next( deviceConfigIter ); + _pAwsIotOnboardingDecoder->stepOut( deviceConfigIter, &deviceConfigurationDecoder ); + + _pAwsIotOnboardingDecoder->destroy( &deviceConfigInnerKeyDecoder ); + _pAwsIotOnboardingDecoder->destroy( &deviceConfigInnerValueDecoder ); + + IotLogError( "Invalid data type for device configuration entry received within server response for %s operation. Expected data" + "type is text string for both keys and values.", + GET_ONBOARD_DEVICE_OPERATION_LOG ); + + IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_BAD_RESPONSE ); + } + pDeviceConfigurationList[ configurationListIndex ].pKey = ( const char * ) deviceConfigInnerKeyDecoder.u.value.u.string.pString; pDeviceConfigurationList[ configurationListIndex ].keyLength = deviceConfigInnerKeyDecoder.u.value.u.string.length; @@ -453,6 +516,7 @@ AwsIotOnboardingError_t _AwsIotOnboarding_ParseOnboardDeviceResponse( AwsIotStat } } + /* Look for the Thing Name entry. */ decoderStatus = _pAwsIotOnboardingDecoder->find( &payloadDecoder, ONBOARDING_ONBOARD_DEVICE_RESPONSE_PAYLOAD_THING_NAME_STRING, &thingNameDecoder ); @@ -475,11 +539,52 @@ AwsIotOnboardingError_t _AwsIotOnboarding_ParseOnboardDeviceResponse( AwsIotStat if( thingNameDecoder.type != IOT_SERIALIZER_SCALAR_TEXT_STRING ) { IotLogError( - "Invalid \"%s\" data received server response for %s operation. Value is expected to be a text string.", + "Invalid \"%s\" data received in server response for %s operation. Value is expected to be a text string.", ONBOARDING_ONBOARD_DEVICE_RESPONSE_PAYLOAD_THING_NAME_STRING, GET_ONBOARD_DEVICE_OPERATION_LOG ); IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_BAD_RESPONSE ); } + + /* Populate information for the "Thing Name" string. */ + userCallbackParam.u.acceptedResponse.pThingName = ( const char * ) + thingNameDecoder.u.value.u.string.pString; + userCallbackParam.u.acceptedResponse.thingNameLength = thingNameDecoder.u.value.u.string.length; + } + + /* Look for the client ID entry. */ + decoderStatus = _pAwsIotOnboardingDecoder->find( &payloadDecoder, + ONBOARDING_ONBOARD_DEVICE_RESPONSE_PAYLOAD_CLIENT_ID_STRING, + &clientIdDecoder ); + + /* Client ID entry NOT found in payload. */ + if( decoderStatus != IOT_SERIALIZER_SUCCESS ) + { + IotLogError( + "Client ID entry (searched with \"%s\" key) NOT present in server response for %s operation", + ONBOARDING_ONBOARD_DEVICE_RESPONSE_PAYLOAD_CLIENT_ID_STRING, + GET_ONBOARD_DEVICE_OPERATION_LOG ); + IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_BAD_RESPONSE ); + } + else if( decoderStatus != IOT_SERIALIZER_SUCCESS ) + { + IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_INTERNAL_FAILURE ); + } + /* Client ID entry FOUND in payload. */ + else + { + if( clientIdDecoder.type != IOT_SERIALIZER_SCALAR_TEXT_STRING ) + { + IotLogError( + "Invalid \"%s\" data received in server response for %s operation. Value is expected to be a text string.", + ONBOARDING_ONBOARD_DEVICE_RESPONSE_PAYLOAD_CLIENT_ID_STRING, + GET_ONBOARD_DEVICE_OPERATION_LOG ); + IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_BAD_RESPONSE ); + } + + /* Populate information for the "Client ID" data. */ + userCallbackParam.u.acceptedResponse.pClientId = ( const char * ) + clientIdDecoder.u.value.u.string.pString; + userCallbackParam.u.acceptedResponse.clientIdLength = clientIdDecoder.u.value.u.string.length; } /* Populate the status code information to represent success response from the server. */ @@ -489,11 +594,6 @@ AwsIotOnboardingError_t _AwsIotOnboarding_ParseOnboardDeviceResponse( AwsIotStat userCallbackParam.u.acceptedResponse.pDeviceConfigList = pDeviceConfigurationList; userCallbackParam.u.acceptedResponse.numOfConfigurationEntries = numOfDeviceConfigurationEntries; - /* Populate information for the "Thing Name" string. */ - userCallbackParam.u.acceptedResponse.pThingName = ( const char * ) - thingNameDecoder.u.value.u.string.pString; - userCallbackParam.u.acceptedResponse.thingNameLength = thingNameDecoder.u.value.u.string.length; - /* Invoke the user-provided callback with the parsed credentials data . */ userCallbackInfo->onboardDeviceCallback.function( userCallbackInfo->onboardDeviceCallback.userParam, &userCallbackParam ); @@ -526,6 +626,7 @@ AwsIotOnboardingError_t _AwsIotOnboarding_ParseOnboardDeviceResponse( AwsIotStat _pAwsIotOnboardingDecoder->destroy( &deviceConfigurationDecoder ); _pAwsIotOnboardingDecoder->destroy( &thingNameDecoder ); + _pAwsIotOnboardingDecoder->destroy( &clientIdDecoder ); _pAwsIotOnboardingDecoder->destroy( &payloadDecoder ); IOT_FUNCTION_CLEANUP_END(); diff --git a/libraries/aws/onboarding/src/aws_iot_onboarding_serializer.c b/libraries/aws/onboarding/src/aws_iot_onboarding_serializer.c index 6f1601eac5..d50108204d 100644 --- a/libraries/aws/onboarding/src/aws_iot_onboarding_serializer.c +++ b/libraries/aws/onboarding/src/aws_iot_onboarding_serializer.c @@ -138,6 +138,7 @@ AwsIotOnboardingError_t _AwsIotOnboarding_SerializeOnboardDeviceRequestPayload( size_t bufferSize ) { AwsIotOnboarding_Assert( ( pRequestData->pDeviceCertificateId != NULL ) && ( pRequestData->deviceCertificateIdLength > 0 ) ); + AwsIotOnboarding_Assert( ( pRequestData->pCertificateOwnershipToken != NULL ) && ( pRequestData->ownershipTokenLength > 0 ) ); AwsIotOnboarding_Assert( ( ( pRequestData->pParametersStart == NULL ) && ( pRequestData->pParametersStart == 0 ) ) || ( ( pRequestData->pParametersStart != NULL ) && ( pRequestData->numOfParameters > 0 ) ) ); @@ -146,13 +147,27 @@ AwsIotOnboardingError_t _AwsIotOnboarding_SerializeOnboardDeviceRequestPayload( IotSerializerEncoderObject_t outerMapEncoder = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_MAP; IotSerializerScalarData_t certificateIdData = IotSerializer_ScalarTextStringWithLength( pRequestData->pDeviceCertificateId, pRequestData->deviceCertificateIdLength ); + IotSerializerScalarData_t certificateTokenData = IotSerializer_ScalarTextStringWithLength( pRequestData->pCertificateOwnershipToken, + pRequestData->ownershipTokenLength ); IotSerializerEncoderObject_t parametersMapEncoder = IOT_SERIALIZER_ENCODER_CONTAINER_INITIALIZER_MAP; - size_t numOfPayloadEntries = 2; + size_t numOfPayloadEntries = 0; const AwsIotOnboardingRequestParameterEntry_t * pParametersList = pRequestData->pParametersStart; char * pParameterKeyCopy = NULL; IotSerializerScalarData_t parameterValueData; IotSerializerError_t serializerStatus = IOT_SERIALIZER_SUCCESS; + /* If no parameters have been provided, then the payload map container will contain only 2 entries, one for + * certificate ID and the other for for the certificate ownership token string.*/ + if( pParametersList == NULL ) + { + numOfPayloadEntries = 2; + } + /* Otherwise, account for the entry of parameters as well in the count. */ + else + { + numOfPayloadEntries = 3; + } + /* Determine the status checking expression logic for the serializer error code based on whether the serialization * buffer has been provided. This is done to accommodate #IOT_SERIALIZER_BUFFER_TOO_SMALL error when no * serialization buffer is provided. */ @@ -178,19 +193,12 @@ AwsIotOnboardingError_t _AwsIotOnboarding_SerializeOnboardDeviceRequestPayload( IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_INTERNAL_FAILURE ); } - /* If no parameters have been passed, then the payload map will contain only a single entry (i.e. for certificate). - * */ - if( pParametersList == NULL ) - { - numOfPayloadEntries = 1; - } - /* Create the outermost map that will contain "certificate" and "parameters" (optional) entries .*/ serializerStatus = _pAwsIotOnboardingEncoder->openContainer( pOutermostEncoder, &outerMapEncoder, numOfPayloadEntries ); - /* Insert the entry for the "certificate". */ + /* Insert the entry for the "certificate ID". */ if( checkSerializerStatus( _pAwsIotOnboardingEncoder->appendKeyValue( &outerMapEncoder, ONBOARDING_ONBOARD_DEVICE_REQUEST_PAYLOAD_CERTIFICATE_ID_STRING, certificateIdData ) ) == false ) @@ -201,6 +209,17 @@ AwsIotOnboardingError_t _AwsIotOnboarding_SerializeOnboardDeviceRequestPayload( IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_INTERNAL_FAILURE ); } + /* Insert the entry for the "certificate ownership token". */ + if( checkSerializerStatus( _pAwsIotOnboardingEncoder->appendKeyValue( &outerMapEncoder, + ONBOARDING_ONBOARD_DEVICE_REQUEST_PAYLOAD_CERTIFICATE_TOKEN_STRING, + certificateTokenData ) ) == false ) + { + IotLogError( "Failed to encode entry keyed by %s in request payload of %s operation", + ONBOARDING_ONBOARD_DEVICE_REQUEST_PAYLOAD_CERTIFICATE_TOKEN_STRING, + GET_ONBOARD_DEVICE_OPERATION_LOG ); + IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_INTERNAL_FAILURE ); + } + /* Insert entry for "parameters" data which will entail inserting a nested map. */ if( pParametersList != NULL ) { @@ -260,14 +279,14 @@ AwsIotOnboardingError_t _AwsIotOnboarding_SerializeOnboardDeviceRequestPayload( /* Close the nested map container */ if( checkSerializerStatus( _pAwsIotOnboardingEncoder->closeContainer( &outerMapEncoder, ¶metersMapEncoder ) ) == false ) { - IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_BAD_PARAMETER ); + IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_INTERNAL_FAILURE ); } } /* Close the map. */ if( checkSerializerStatus( _pAwsIotOnboardingEncoder->closeContainer( pOutermostEncoder, &outerMapEncoder ) ) == false ) { - IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_BAD_PARAMETER ); + IOT_SET_AND_GOTO_CLEANUP( AWS_IOT_ONBOARDING_INTERNAL_FAILURE ); } IOT_FUNCTION_EXIT_NO_CLEANUP(); diff --git a/libraries/aws/onboarding/src/private/aws_iot_onboarding_internal.h b/libraries/aws/onboarding/src/private/aws_iot_onboarding_internal.h index 97706806b1..ee6294a4fc 100644 --- a/libraries/aws/onboarding/src/private/aws_iot_onboarding_internal.h +++ b/libraries/aws/onboarding/src/private/aws_iot_onboarding_internal.h @@ -254,17 +254,22 @@ /** * @brief The key for the device certificate entry in the response payload of the GetDeviceCredentials service API. */ -#define ONBOARDING_GET_DEVICE_CREDENTIALS_RESPONSE_PAYLOAD_CERTIFICATE_PEM_STRING "certificatePem" +#define ONBOARDING_GET_DEVICE_CREDENTIALS_RESPONSE_PAYLOAD_CERTIFICATE_PEM_STRING "certificatePem" /** * @brief The key for the certificate Id entry in the response payload of the GetDeviceCredentials service API. */ -#define ONBOARDING_GET_DEVICE_CREDENTIALS_RESPONSE_PAYLOAD_CERTIFICATE_ID_STRING "certificateId" +#define ONBOARDING_GET_DEVICE_CREDENTIALS_RESPONSE_PAYLOAD_CERTIFICATE_ID_STRING "certificateId" /** * @brief The key for the private key entry in the response payload of the GetDeviceCredentials service API. */ -#define ONBOARDING_GET_DEVICE_CREDENTIALS_RESPONSE_PAYLOAD_PRIVATE_KEY_STRING "privateKey" +#define ONBOARDING_GET_DEVICE_CREDENTIALS_RESPONSE_PAYLOAD_PRIVATE_KEY_STRING "privateKey" + +/** + * @brief The key for the token key entry in the response payload of the GetDeviceCredentials service API. + */ +#define ONBOARDING_GET_DEVICE_CREDENTIALS_RESPONSE_PAYLOAD_CERTIFICATE_TOKEN_KEY_STRING "certificateOwnershipToken" /** * @brief The common path in the request and response MQTT topics of the OnboardDevice service API. @@ -309,7 +314,15 @@ * * @note This should be used in serializing the request payload for sending to the server. */ -#define ONBOARDING_ONBOARD_DEVICE_REQUEST_PAYLOAD_CERTIFICATE_ID_STRING "certificateId" +#define ONBOARDING_ONBOARD_DEVICE_REQUEST_PAYLOAD_CERTIFICATE_ID_STRING "certificateId" + +/** + * @brief The key for the certificate ownership token's entry to be inserted in the request payload for the + * provisioning the device. + * + * @note This should be used in serializing the request payload for sending to the server. + */ +#define ONBOARDING_ONBOARD_DEVICE_REQUEST_PAYLOAD_CERTIFICATE_TOKEN_STRING "certificateOwnershipToken" /** * @brief The key for the device context data's entry to be inserted in the request payload for the OnboardDevice @@ -318,7 +331,7 @@ * @note This should be used in serializing the request payload for sending to the server, only if the calling * application provides valid device context data. */ -#define ONBOARDING_ONBOARD_DEVICE_REQUEST_PAYLOAD_PARAMETERS_STRING "deviceContext" +#define ONBOARDING_ONBOARD_DEVICE_REQUEST_PAYLOAD_PARAMETERS_STRING "parameters" /** * @brief The length of the MQTT request topic filter of the OnboardDevice service API. @@ -337,37 +350,44 @@ /** * @brief The key for the device configuration data's entry in the response payload of the OnboardDevice service API. * - * @note This should be utilized in parsing the response payload received from the server. + * @note This should be utilized in parsing the success case response payload received from the server. */ #define ONBOARDING_ONBOARD_DEVICE_RESPONSE_PAYLOAD_DEVICE_CONFIGURATION_STRING "deviceConfiguration" /** * @brief The key for the Thing resource name's entry in the response payload of the OnboardDevice service API. * - * @note This should be utilized in parsing the response payload received from the server. + * @note This should be utilized in parsing the success case response payload received from the server. */ #define ONBOARDING_ONBOARD_DEVICE_RESPONSE_PAYLOAD_THING_NAME_STRING "thingName" +/** + * @brief The key for the connection client ID's entry in the response payload of the OnboardDevice service API. + * + * @note This should be utilized in parsing the success case response payload received from the server. + */ +#define ONBOARDING_ONBOARD_DEVICE_RESPONSE_PAYLOAD_CLIENT_ID_STRING "clientId" + /** * @brief The key for the status code entry in the "rejected" response payload from the server. * * @note This should be utilized in parsing the response payload received from the server. */ -#define ONBOARDING_REJECTED_RESPONSE_STATUS_CODE_STRING "StatusCode" +#define ONBOARDING_REJECTED_RESPONSE_STATUS_CODE_STRING "statusCode" /** * @brief The key for the error code entry in the "rejected" response payload from the server. * * @note This should be utilized in parsing the response payload received from the server. */ -#define ONBOARDING_REJECTED_RESPONSE_ERROR_CODE_STRING "ErrorCode" +#define ONBOARDING_REJECTED_RESPONSE_ERROR_CODE_STRING "errorCode" /** * @brief The key for the status message entry in the "rejected" response payload from the server. * * @note This should be utilized in parsing the response payload received from the server. */ -#define ONBOARDING_REJECTED_RESPONSE_ERROR_MESSAGE_STRING "ErrorMessage" +#define ONBOARDING_REJECTED_RESPONSE_ERROR_MESSAGE_STRING "errorMessage" diff --git a/libraries/aws/onboarding/test/system/aws_iot_tests_onboarding_system.c b/libraries/aws/onboarding/test/system/aws_iot_tests_onboarding_system.c index 362f9a92ea..505d97af7b 100644 --- a/libraries/aws/onboarding/test/system/aws_iot_tests_onboarding_system.c +++ b/libraries/aws/onboarding/test/system/aws_iot_tests_onboarding_system.c @@ -108,17 +108,26 @@ static IotMqttConnection_t _mqttConnection = IOT_MQTT_CONNECTION_INITIALIZER; */ static const char * _pTestMqttClientId = AWS_IOT_TEST_PROVISIONING_CLIENT_ID; -/** - * @brief Certificate ID for OnboardDevice API tests. - */ -static char _testCertificateId[] = AWS_IOT_TEST_PROVISIONING_CERTIFICATE_ID; - /** * @brief Parameters to use for testing the OnboardDevice API. */ static const AwsIotOnboardingRequestParameterEntry_t _pTestParameters[] = AWS_IOT_TEST_ONBOARDING_TEMPLATE_PARAMETERS; + +/** + * @brief Type for the context parameter for the #AwsIotOnboarding_DeviceCredentialsCallbackInfo_t callback. + * It will be used for storing the received Certificate ID and the ownership token data received from the server through + * the callback, so that can be used for provisioning the demo application. + */ +typedef struct _deviceCredentialsCallbackContext +{ + char * pCertificateIdBuffer; + size_t certificateIdLength; + char * pCertificateOwnershipToken; + size_t tokenLength; +} _deviceCredentialsCallbackContext_t; + /*-----------------------------------------------------------*/ /** @@ -154,11 +163,13 @@ static void _printDeviceCredentialsCallback( void * contextParam, TEST_ASSERT_NOT_NULL( pResponseInfo->u.acceptedResponse.pPrivateKey ); TEST_ASSERT_GREATER_THAN( 0, pResponseInfo->u.acceptedResponse.privateKeyLength ); - IotLogInfo( "\n Certificate PEM = %.*s\n Certificate ID = %.*s\n DREADED PRIVATE KEY = %.*s\n", + IotLogInfo( "\n Certificate PEM = %.*s\n Certificate ID = %.*s\n Ownership Token = %.*s\n DREADED PRIVATE KEY = %.*s\n", pResponseInfo->u.acceptedResponse.deviceCertificateLength, pResponseInfo->u.acceptedResponse.pDeviceCertificate, pResponseInfo->u.acceptedResponse.certificateIdLength, pResponseInfo->u.acceptedResponse.pCertificateId, + pResponseInfo->u.acceptedResponse.ownershipTokenLength, + pResponseInfo->u.acceptedResponse.pCertificateOwnershipToken, pResponseInfo->u.acceptedResponse.privateKeyLength, pResponseInfo->u.acceptedResponse.pPrivateKey ); } @@ -170,6 +181,61 @@ static void _printDeviceCredentialsCallback( void * contextParam, /*-----------------------------------------------------------*/ +static void _storeCertificateDataForProvisioning( void * contextParam, + const AwsIotOnboardingGetDeviceCredentialsResponse_t * pResponseInfo ) +{ + _deviceCredentialsCallbackContext_t * certificateIdTokenContext = + ( _deviceCredentialsCallbackContext_t * ) contextParam; + + IotLogInfo( "Received StatusCode={%d}", pResponseInfo->statusCode ); + + if( pResponseInfo->statusCode == AWS_IOT_ONBOARDING_SERVER_STATUS_ACCEPTED ) + { + /* Allocate buffer space for storing the certificate ID obtained from the server. */ + certificateIdTokenContext->pCertificateIdBuffer = + Iot_DefaultMalloc( pResponseInfo->u.acceptedResponse.certificateIdLength + 1 ); + + /* Copy the certificate ID into the buffer. */ + if( certificateIdTokenContext->pCertificateIdBuffer != NULL ) + { + /* Copy the size of the Certificate ID string. */ + certificateIdTokenContext->certificateIdLength = pResponseInfo->u.acceptedResponse.certificateIdLength; + + memcpy( certificateIdTokenContext->pCertificateIdBuffer, + pResponseInfo->u.acceptedResponse.pCertificateId, + pResponseInfo->u.acceptedResponse.certificateIdLength ); + /* Add a NULL terminator to the buffer (to treat the buffer as a string!) */ + *( certificateIdTokenContext->pCertificateIdBuffer + pResponseInfo->u.acceptedResponse.certificateIdLength ) = '\0'; + } + + /* Allocate buffer space for storing the ownership token string obtained from the server. */ + certificateIdTokenContext->pCertificateOwnershipToken = + Iot_DefaultMalloc( pResponseInfo->u.acceptedResponse.ownershipTokenLength + 1 ); + + /* Copy the ownership token into the buffer. */ + if( certificateIdTokenContext->pCertificateOwnershipToken != NULL ) + { + /* Copy the size of the ownership token string. */ + certificateIdTokenContext->tokenLength = pResponseInfo->u.acceptedResponse.ownershipTokenLength; + + memcpy( certificateIdTokenContext->pCertificateOwnershipToken, + pResponseInfo->u.acceptedResponse.pCertificateOwnershipToken, + pResponseInfo->u.acceptedResponse.ownershipTokenLength ); + /* Add a NULL terminator to the buffer (to treat the buffer as a string!) */ + *( certificateIdTokenContext->pCertificateOwnershipToken + pResponseInfo->u.acceptedResponse.ownershipTokenLength ) = '\0'; + } + + /* We don't need the rest of the data for provisioning the test. */ + } + else + { + IotLogInfo( "Request for new credentials was rejected! Test will be aborted." ); + AwsIotOnboarding_Assert( false ); + } +} + +/*-----------------------------------------------------------*/ + /** * @brief User callback function for printing parsed response data sent by the OnboardDevice service API. */ @@ -183,14 +249,18 @@ static void _printOnboardDeviceResponseCallback( void * contextParam, if( pResponseInfo->statusCode == AWS_IOT_ONBOARDING_SERVER_STATUS_ACCEPTED ) { + if( pResponseInfo->u.acceptedResponse.pClientId != NULL ) + { + IotLogInfo( "ClientID = %.*s", + pResponseInfo->u.acceptedResponse.clientIdLength, + pResponseInfo->u.acceptedResponse.pClientId ); + } + if( pResponseInfo->u.acceptedResponse.pThingName != NULL ) { - if( pResponseInfo->u.acceptedResponse.pThingName != NULL ) - { - IotLogInfo( "ThingName = %.*s", - pResponseInfo->u.acceptedResponse.thingNameLength, - pResponseInfo->u.acceptedResponse.pThingName ); - } + IotLogInfo( "ThingName = %.*s", + pResponseInfo->u.acceptedResponse.thingNameLength, + pResponseInfo->u.acceptedResponse.pThingName ); } if( pResponseInfo->u.acceptedResponse.numOfConfigurationEntries > 0 ) @@ -375,16 +445,40 @@ TEST( Onboarding_System, OnboardDeviceNominalCase ) { AwsIotOnboardingError_t status = AWS_IOT_ONBOARDING_SUCCESS; - AwsIotOnboardingOnboardDeviceCallbackInfo_t callbackInfo = + AwsIotOnboardingOnboardDeviceCallbackInfo_t onboardDeviceCallback = { .userParam = NULL, .function = _printOnboardDeviceResponseCallback }; + /* To test the OnboardDevice API, we need to request credentials from the GetDeviceCredentials API, that */ + /* we will use for provisioning in the test. */ + + _deviceCredentialsCallbackContext_t newCertificateContext; + + newCertificateContext.pCertificateIdBuffer = NULL; + newCertificateContext.certificateIdLength = 0; + newCertificateContext.pCertificateOwnershipToken = NULL; + newCertificateContext.tokenLength = 0; + + AwsIotOnboardingGetDeviceCredentialsCallbackInfo_t createNewCredsCallback = + { + .userParam = &newCertificateContext, + .function = _storeCertificateDataForProvisioning + }; + + /* Obtain new certificate and ownership token for testing provisioning API. */ + status = AwsIotOnboarding_GetDeviceCredentials( _mqttConnection, + 0, + AWS_IOT_TEST_ONBOARDING_TIMEOUT, + &createNewCredsCallback ); + AwsIotOnboardingOnboardDeviceRequestInfo_t requestInfo; - requestInfo.pDeviceCertificateId = _testCertificateId; - requestInfo.deviceCertificateIdLength = sizeof( _testCertificateId ) - 1; + requestInfo.pDeviceCertificateId = newCertificateContext.pCertificateIdBuffer; + requestInfo.deviceCertificateIdLength = newCertificateContext.certificateIdLength; + requestInfo.pCertificateOwnershipToken = newCertificateContext.pCertificateOwnershipToken; + requestInfo.ownershipTokenLength = newCertificateContext.tokenLength; requestInfo.pTemplateName = AWS_IOT_TEST_ONBOARDING_TEMPLATE_NAME; requestInfo.templateNameLength = ( sizeof( AWS_IOT_TEST_ONBOARDING_TEMPLATE_NAME ) - 1 ); requestInfo.pParametersStart = _pTestParameters; @@ -395,8 +489,14 @@ TEST( Onboarding_System, OnboardDeviceNominalCase ) status = AwsIotOnboarding_OnboardDevice( _mqttConnection, &requestInfo, AWS_IOT_TEST_ONBOARDING_TIMEOUT, - &callbackInfo ); + &onboardDeviceCallback ); TEST_ASSERT_EQUAL( AWS_IOT_ONBOARDING_SUCCESS, status ); + + /* Test Cleanup */ + + /* Release the certificate data buffers. */ + Iot_DefaultFree( newCertificateContext.pCertificateIdBuffer ); + Iot_DefaultFree( newCertificateContext.pCertificateOwnershipToken ); } diff --git a/libraries/aws/onboarding/test/unit/aws_iot_tests_onboarding_api.c b/libraries/aws/onboarding/test/unit/aws_iot_tests_onboarding_api.c index 0274d5ef61..e5d83acd88 100644 --- a/libraries/aws/onboarding/test/unit/aws_iot_tests_onboarding_api.c +++ b/libraries/aws/onboarding/test/unit/aws_iot_tests_onboarding_api.c @@ -170,7 +170,7 @@ static const char * _getDeviceCredentialsAcceptedResponseTopic = */ static const uint8_t _sampleGetDeviceCredentialsServerResponsePayload[] = { - 0xA3, /* # map( 2 ) */ + 0xA4, /* # map( 4 ) */ 0x6E, /* # text( 14 ) */ 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x65, 0x6D, /* # "certificatePem" */ 0x67, /* # text(7) */ @@ -182,7 +182,13 @@ static const uint8_t _sampleGetDeviceCredentialsServerResponsePayload[] = 0x6A, /* # text( 10 ) */ 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4B, 0x65, 0x79, /* # "privateKey" */ 0x67, /* # text(7) */ - 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x21 /*# "Secret!" */ + 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x21, /*# "Secret!" */ + 0x78, 0x19, /*# text(25) */ + 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x4F, 0x77, 0x6E, + 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x54, 0x6F, 0x6B, 0x65, 0x6E, /*# "certificateOwnershipToken" + * */ + 0x66, /*# text(6) */ + 0x54, 0x6F, 0x6B, 0x65, 0x6E, 0x21 /*# "Token!" */ }; /** @@ -190,16 +196,19 @@ static const uint8_t _sampleGetDeviceCredentialsServerResponsePayload[] = */ static AwsIotOnboardingGetDeviceCredentialsResponse_t _expectedGetDeviceCredentialsCallbackParams = { - .statusCode = AWS_IOT_ONBOARDING_SERVER_STATUS_ACCEPTED, - .u.acceptedResponse.pDeviceCertificate = ( const char * ) - &_sampleGetDeviceCredentialsServerResponsePayload[ 17 ], - .u.acceptedResponse.deviceCertificateLength = 7, - .u.acceptedResponse.pCertificateId = ( const char * ) - &_sampleGetDeviceCredentialsServerResponsePayload[ 39 ], - .u.acceptedResponse.certificateIdLength = 6, - .u.acceptedResponse.pPrivateKey = ( const char * ) - &_sampleGetDeviceCredentialsServerResponsePayload[ 57 ], - .u.acceptedResponse.privateKeyLength = 7 + .statusCode = AWS_IOT_ONBOARDING_SERVER_STATUS_ACCEPTED, + .u.acceptedResponse.pDeviceCertificate = ( const char * ) + &_sampleGetDeviceCredentialsServerResponsePayload[ 17 ], + .u.acceptedResponse.deviceCertificateLength = 7, + .u.acceptedResponse.pCertificateId = ( const char * ) + &_sampleGetDeviceCredentialsServerResponsePayload[ 39 ], + .u.acceptedResponse.certificateIdLength = 6, + .u.acceptedResponse.pPrivateKey = ( const char * ) + &_sampleGetDeviceCredentialsServerResponsePayload[ 57 ], + .u.acceptedResponse.privateKeyLength = 7, + .u.acceptedResponse.pCertificateOwnershipToken = ( const char * ) + &_sampleGetDeviceCredentialsServerResponsePayload[ 92 ], + .u.acceptedResponse.ownershipTokenLength = 6, }; /** @@ -215,23 +224,28 @@ static const AwsIotOnboardingGetDeviceCredentialsCallbackInfo_t _acceptedRespons /** * @brief Certificate ID for OnboardDevice API tests. */ -static const char * _testCertificateId = "TestCertificateID"; +static const char _testCertificateId[] = "TestCertificateID"; + +/** + * @brief Token string for OnboardDevice API tests. + */ +static const char _testCertificateToken[] = "TestToken"; /** * @brief Template ID for OnboardDevice API tests. */ -#define _testTemplateId "123456789123456789123456789123456789" +#define _testTemplateName "TEST_TEMPLATE" /** * @brief The rejected response topic for the OnboardDevice service API. */ -static const char * _onboardDeviceAcceptedResponseTopic = "$aws/provisioning-templates/"_testTemplateId +static const char * _onboardDeviceAcceptedResponseTopic = "$aws/provisioning-templates/"_testTemplateName "/provision/cbor/accepted"; /** * @brief The accepted response topic for the OnboardDevice service API. */ -static const char * _onboardDeviceRejectedResponseTopic = "$aws/provisioning-templates/"_testTemplateId +static const char * _onboardDeviceRejectedResponseTopic = "$aws/provisioning-templates/"_testTemplateName "/provision/cbor/rejected"; /** @@ -240,7 +254,7 @@ static const char * _onboardDeviceRejectedResponseTopic = "$aws/provisioning-tem */ static const uint8_t _sampleOnboardDeviceResponsePayload[] = { - 0xA2, /* # map(2) */ + 0xA3, /* # map(2) */ 0x73, /* # text(19) */ 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6F, 0x6E, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, /* # "deviceConfiguration" */ @@ -260,7 +274,11 @@ static const uint8_t _sampleOnboardDeviceResponsePayload[] = 0x69, /* # text(9) */ 0x74, 0x68, 0x69, 0x6E, 0x67, 0x4E, 0x61, 0x6D, 0x65, /* # "thingName" */ 0x69, /* # text(9) */ - 0x54, 0x65, 0x73, 0x74, 0x54, 0x68, 0x69, 0x6E, 0x67 /* # "TestThing" */ + 0x54, 0x65, 0x73, 0x74, 0x54, 0x68, 0x69, 0x6E, 0x67, /* # "TestThing" */ + 0x68, /*# text(8) */ + 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x49, 0x64, /*# "clientId" */ + 0x65, /*# text(5) */ + 0x44, 0x75, 0x6D, 0x6D, 0x79 /*# "Dummy" */ }; static const AwsIotOnboardingResponseDeviceConfigurationEntry_t _expectedDeviceConfigList[] = @@ -288,6 +306,8 @@ static AwsIotOnboardingOnboardDeviceResponse_t _expectedOnboardDeviceCallbackPar .statusCode = AWS_IOT_ONBOARDING_SERVER_STATUS_ACCEPTED, .u.acceptedResponse.pThingName = ( const char * ) &_sampleOnboardDeviceResponsePayload[ 113 ], .u.acceptedResponse.thingNameLength = 9, + .u.acceptedResponse.pClientId = ( const char * ) &_sampleOnboardDeviceResponsePayload[ 132 ], + .u.acceptedResponse.clientIdLength = 5, .u.acceptedResponse.pDeviceConfigList = _expectedDeviceConfigList, .u.acceptedResponse.numOfConfigurationEntries = sizeof( _expectedDeviceConfigList ) / sizeof( AwsIotOnboardingResponseDeviceConfigurationEntry_t ) @@ -310,14 +330,14 @@ static const uint8_t _sampleRejectedServerResponsePayload[] = { 0xA3, /*# map(3) */ 0x6A, /*# text(10) */ - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6F, 0x64, 0x65, /*# "StatusCode" */ + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6F, 0x64, 0x65, /*# "statusCode" */ 0x19, 0x01, 0xF4, /*# unsigned(500) */ 0x69, /*# text(9) */ - 0x45, 0x72, 0x72, 0x6F, 0x72, 0x43, 0x6F, 0x64, 0x65, /*# "ErrorCode" */ + 0x65, 0x72, 0x72, 0x6F, 0x72, 0x43, 0x6F, 0x64, 0x65, /*# "errorCode" */ 0x6E, /*# text(14) */ 0x49, 0x6E, 0x76, 0x61, 0x6C, 0x69, 0x64, 0x50, 0x61, 0x79, 0x6C, 0x6F, 0x61, 0x64, /*# "InvalidPayload" */ 0x6C, /*# text(12) */ - 0x45, 0x72, 0x72, 0x6F, 0x72, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, /*# "ErrorMessage" */ + 0x65, 0x72, 0x72, 0x6F, 0x72, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, /*# "errorMessage" */ 0x65, /*# text(5) */ 0x4F, 0x6F, 0x70, 0x73, 0x21, /*# "Oops!" */ }; @@ -373,6 +393,12 @@ static void _testGetDeviceCredentialsCallback( void * contextParam, pExpectedParams->u.acceptedResponse.pPrivateKey, pResponseInfo->u.acceptedResponse.pPrivateKey, pExpectedParams->u.acceptedResponse.privateKeyLength ) ); + AwsIotOnboarding_Assert( pExpectedParams->u.acceptedResponse.ownershipTokenLength == + pResponseInfo->u.acceptedResponse.ownershipTokenLength ); + AwsIotOnboarding_Assert( 0 == memcmp( + pExpectedParams->u.acceptedResponse.pCertificateOwnershipToken, + pResponseInfo->u.acceptedResponse.pCertificateOwnershipToken, + pExpectedParams->u.acceptedResponse.ownershipTokenLength ) ); break; default: @@ -404,6 +430,18 @@ static void _testOnboardDeviceCallback( void * contextParam, pExpectedParams->u.acceptedResponse.thingNameLength ) ); } + AwsIotOnboarding_Assert( + pExpectedParams->u.acceptedResponse.clientIdLength == + pResponseInfo->u.acceptedResponse.clientIdLength ); + + if( pExpectedParams->u.acceptedResponse.clientIdLength > 0 ) + { + AwsIotOnboarding_Assert( 0 == memcmp( + pExpectedParams->u.acceptedResponse.pClientId, + pResponseInfo->u.acceptedResponse.pClientId, + pExpectedParams->u.acceptedResponse.clientIdLength ) ); + } + AwsIotOnboarding_Assert( pExpectedParams->u.acceptedResponse.numOfConfigurationEntries == pResponseInfo->u.acceptedResponse.numOfConfigurationEntries ); @@ -507,9 +545,11 @@ static void _testOnboardDeviceAPIWithServerResponse( _serverResponseThreadContex 0 ); requestInfo.pDeviceCertificateId = _testCertificateId; - requestInfo.deviceCertificateIdLength = sizeof( _testCertificateId ); - requestInfo.pTemplateName = _testTemplateId; - requestInfo.templateNameLength = strlen( _testTemplateId ); + requestInfo.deviceCertificateIdLength = strlen( _testCertificateId ); + requestInfo.pCertificateOwnershipToken = _testCertificateToken; + requestInfo.ownershipTokenLength = strlen( _testCertificateToken ); + requestInfo.pTemplateName = _testTemplateName; + requestInfo.templateNameLength = strlen( _testTemplateName ); requestInfo.pParametersStart = NULL; requestInfo.numOfParameters = 0; @@ -935,12 +975,14 @@ TEST( Onboarding_Unit_API, OnboardDeviceAPIInvalidParameters ) AwsIotOnboardingOnboardDeviceRequestInfo_t requestInfo = { - .pDeviceCertificateId = _testCertificateId, - .deviceCertificateIdLength = strlen( _testCertificateId ), - .pTemplateName = _testTemplateId, - .templateNameLength = strlen( _testTemplateId ), - .pParametersStart = NULL, - .numOfParameters = 0, + .pDeviceCertificateId = _testCertificateId, + .deviceCertificateIdLength = strlen( _testCertificateId ), + .pCertificateOwnershipToken = _testCertificateToken, + .ownershipTokenLength = strlen( _testCertificateToken ), + .pTemplateName = _testTemplateName, + .templateNameLength = strlen( _testTemplateName ), + .pParametersStart = NULL, + .numOfParameters = 0, }; /* Uninitialized MQTT connection. */ @@ -981,9 +1023,22 @@ TEST( Onboarding_Unit_API, OnboardDeviceAPIInvalidParameters ) &callbackInfo ); TEST_ASSERT_EQUAL( AWS_IOT_ONBOARDING_BAD_PARAMETER, status ); - /* Invalid template ID in request data. Re-assign certificate data in object. */ requestInfo.pDeviceCertificateId = _testCertificateId; requestInfo.deviceCertificateIdLength = sizeof( _testCertificateId ); + + /* Invalid certificate token string in request. */ + requestInfo.pCertificateOwnershipToken = NULL; + requestInfo.ownershipTokenLength = 0; + status = AwsIotOnboarding_OnboardDevice( _pMqttConnection, + &requestInfo, + 0, + &callbackInfo ); + TEST_ASSERT_EQUAL( AWS_IOT_ONBOARDING_BAD_PARAMETER, status ); + + requestInfo.ownershipTokenLength = strlen( _testCertificateToken ); + requestInfo.pTemplateName = _testTemplateName; + + /* Invalid template ID in request data. Re-assign certificate data in object. */ requestInfo.pTemplateName = NULL; requestInfo.templateNameLength = 0; status = AwsIotOnboarding_OnboardDevice( _pMqttConnection, @@ -1007,12 +1062,14 @@ TEST( Onboarding_Unit_API, OnboardDeviceAPICalledWithoutInit ) const AwsIotOnboardingOnboardDeviceRequestInfo_t requestInfo = { - .pDeviceCertificateId = _testCertificateId, - .deviceCertificateIdLength = sizeof( _testCertificateId ), - .pTemplateName = _testTemplateId, - .templateNameLength = strlen( _testTemplateId ), - .pParametersStart = NULL, - .numOfParameters = 0, + .pDeviceCertificateId = _testCertificateId, + .deviceCertificateIdLength = sizeof( _testCertificateId ), + .pCertificateOwnershipToken = _testCertificateToken, + .ownershipTokenLength = strlen( _testCertificateToken ), + .pTemplateName = _testTemplateName, + .templateNameLength = strlen( _testTemplateName ), + .pParametersStart = NULL, + .numOfParameters = 0, }; /* Reset the library to simulate the test case when the library is not initialized. */ @@ -1044,12 +1101,14 @@ TEST( Onboarding_Unit_API, OnboardDeviceAPINoServerResponse ) const AwsIotOnboardingOnboardDeviceRequestInfo_t requestInfo = { - .pDeviceCertificateId = _testCertificateId, - .deviceCertificateIdLength = sizeof( _testCertificateId ), - .pTemplateName = _testTemplateId, - .templateNameLength = strlen( _testTemplateId ), - .pParametersStart = NULL, - .numOfParameters = 0, + .pDeviceCertificateId = _testCertificateId, + .deviceCertificateIdLength = sizeof( _testCertificateId ), + .pTemplateName = _testTemplateName, + .templateNameLength = strlen( _testTemplateName ), + .pCertificateOwnershipToken = _testCertificateToken, + .ownershipTokenLength = strlen( _testCertificateToken ), + .pParametersStart = NULL, + .numOfParameters = 0, }; @@ -1186,11 +1245,15 @@ TEST( Onboarding_Unit_API, OnboardDeviceAPIServerResponseWithoutDeviceConfigurat { const uint8_t pResponseWithoutDeviceConfigData[] = { - 0xA1, /* # map(1) */ + 0xA2, /* # map(2) */ 0x69, /* # text(9) */ 0x74, 0x68, 0x69, 0x6E, 0x67, 0x4E, 0x61, 0x6D, 0x65, /* # "thingName" */ 0x69, /* # text(9) */ - 0x54, 0x65, 0x73, 0x74, 0x54, 0x68, 0x69, 0x6E, 0x67 /* # "TestThing" */ + 0x54, 0x65, 0x73, 0x74, 0x54, 0x68, 0x69, 0x6E, 0x67, /* # "TestThing" */ + 0x68, /*# text(8) */ + 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x49, 0x64, /*# "clientId" */ + 0x65, /*# text(5) */ + 0x44, 0x75, 0x6D, 0x6D, 0x79 /*# "Dummy" */ }; _serverResponseThreadContext_t serverResponse = @@ -1206,6 +1269,8 @@ TEST( Onboarding_Unit_API, OnboardDeviceAPIServerResponseWithoutDeviceConfigurat .statusCode = AWS_IOT_ONBOARDING_SERVER_STATUS_ACCEPTED, .u.acceptedResponse.pThingName = ( const char * ) &pResponseWithoutDeviceConfigData[ 12 ], .u.acceptedResponse.thingNameLength = 9, + .u.acceptedResponse.pClientId = ( const char * ) &pResponseWithoutDeviceConfigData[ 31 ], + .u.acceptedResponse.clientIdLength = 5, .u.acceptedResponse.pDeviceConfigList = NULL, .u.acceptedResponse.numOfConfigurationEntries = 0 }; @@ -1231,15 +1296,19 @@ TEST( Onboarding_Unit_API, OnboardDeviceAPIServerResponseWithoutThingName ) { const uint8_t pServerResponseWithoutThingName[] = { - 0xA1, /* # map(1) */ - 0x73, /* # text(19) */ + 0xA2, /* # map(2) */ + 0x73, /* # text(19) */ 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6F, 0x6E, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6F, 0x6E, /* # "deviceConfiguration" */ - 0xA1, /* # map(1), */ - 0x61, /* # text(1), */ - 0x31, /* # "1", */ - 0x61, /* # text(1), */ - 0x32 /* # "2" */ + 0x74, 0x69, 0x6F, 0x6E, /* # "deviceConfiguration" */ + 0xA1, /* # map(1), */ + 0x61, /* # text(1), */ + 0x31, /* # "1", */ + 0x61, /* # text(1), */ + 0x32, /* # "2" */ + 0x68, /*# text(8) */ + 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x49, 0x64, /*# "clientId" */ + 0x65, /*# text(5) */ + 0x44, 0x75, 0x6D, 0x6D, 0x79 /*# "Dummy" */ }; _serverResponseThreadContext_t serverResponse = @@ -1265,6 +1334,8 @@ TEST( Onboarding_Unit_API, OnboardDeviceAPIServerResponseWithoutThingName ) .statusCode = AWS_IOT_ONBOARDING_SERVER_STATUS_ACCEPTED, .u.acceptedResponse.pThingName = NULL, .u.acceptedResponse.thingNameLength = 0, + .u.acceptedResponse.pClientId = ( const char * ) &pServerResponseWithoutThingName[ 36 ], + .u.acceptedResponse.clientIdLength = 5, .u.acceptedResponse.pDeviceConfigList = pExpectedDeviceConfigList, .u.acceptedResponse.numOfConfigurationEntries = 1 }; diff --git a/libraries/aws/onboarding/test/unit/aws_iot_tests_onboarding_parser.c b/libraries/aws/onboarding/test/unit/aws_iot_tests_onboarding_parser.c index 437f6c26a8..1cbd6b5659 100644 --- a/libraries/aws/onboarding/test/unit/aws_iot_tests_onboarding_parser.c +++ b/libraries/aws/onboarding/test/unit/aws_iot_tests_onboarding_parser.c @@ -48,14 +48,14 @@ static const uint8_t _sampleRejectedServerResponsePayload[] = { 0xA3, /*# map(3) */ 0x6A, /*# text(10) */ - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6F, 0x64, 0x65, /*# "StatusCode" */ + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6F, 0x64, 0x65, /*# "statusCode" */ 0x19, 0x01, 0xF4, /*# unsigned(500) */ 0x69, /*# text(9) */ - 0x45, 0x72, 0x72, 0x6F, 0x72, 0x43, 0x6F, 0x64, 0x65, /*# "ErrorCode" */ + 0x65, 0x72, 0x72, 0x6F, 0x72, 0x43, 0x6F, 0x64, 0x65, /*# "errorCode" */ 0x6E, /*# text(14) */ 0x49, 0x6E, 0x76, 0x61, 0x6C, 0x69, 0x64, 0x50, 0x61, 0x79, 0x6C, 0x6F, 0x61, 0x64, /*# "InvalidPayload" */ 0x6C, /*# text(12) */ - 0x45, 0x72, 0x72, 0x6F, 0x72, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, /*# "ErrorMessage" */ + 0x65, 0x72, 0x72, 0x6F, 0x72, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, /*# "errorMessage" */ 0x78, 0x2D, /*# text(45) */ 0x4F, 0x6F, 0x70, 0x73, 0x21, 0x20, 0x57, 0x65, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x61, 0x20, 0x6C, 0x6F, 0x6E, 0x67, 0x20, 0x65, 0x72, 0x72, 0x6F, 0x72, @@ -79,9 +79,9 @@ static AwsIotOnboardingRejectedResponse_t _expectedParsedParams = /** * @brief Sample CBOR encoded response payload of device credentials from the server. */ -const uint8_t _sampleDeviceCredentialsResponse[] = +const uint8_t _sampleAcceptedDeviceCredentialsResponse[] = { - 0xA3, /* # map( 3 ) */ + 0xA4, /* # map( 4 ) */ 0x6E, /* # text( 14 ) */ 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x65, 0x6D, /* # "certificatePem" */ 0x67, /* # text(7) */ @@ -93,7 +93,13 @@ const uint8_t _sampleDeviceCredentialsResponse[] = 0x6A, /* # text( 10 ) */ 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4B, 0x65, 0x79, /* # "privateKey" */ 0x67, /* # text(7) */ - 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x21 /*# "Secret!" */ + 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x21, /*# "Secret!" */ + 0x78, 0x19, /*# text(25) */ + 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x4F, 0x77, 0x6E, + 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x54, 0x6F, 0x6B, 0x65, 0x6E, /*# "certificateOwnershipToken" + * */ + 0x66, /*# text(6) */ + 0x54, 0x6F, 0x6B, 0x65, 0x6E, 0x21 /*# "Token!" */ }; /** @@ -102,16 +108,19 @@ const uint8_t _sampleDeviceCredentialsResponse[] = */ AwsIotOnboardingGetDeviceCredentialsResponse_t _expectedDeviceCredentialsParsedParams = { - .statusCode = AWS_IOT_ONBOARDING_SERVER_STATUS_ACCEPTED, - .u.acceptedResponse.pDeviceCertificate = ( const char * ) - &_sampleDeviceCredentialsResponse[ 17 ], - .u.acceptedResponse.deviceCertificateLength = 7, - .u.acceptedResponse.pCertificateId = ( const char * ) - &_sampleDeviceCredentialsResponse[ 39 ], - .u.acceptedResponse.certificateIdLength = 6, - .u.acceptedResponse.pPrivateKey = ( const char * ) - &_sampleDeviceCredentialsResponse[ 57 ], - .u.acceptedResponse.privateKeyLength = 7 + .statusCode = AWS_IOT_ONBOARDING_SERVER_STATUS_ACCEPTED, + .u.acceptedResponse.pDeviceCertificate = ( const char * ) + &_sampleAcceptedDeviceCredentialsResponse[ 17 ], + .u.acceptedResponse.deviceCertificateLength = 7, + .u.acceptedResponse.pCertificateId = ( const char * ) + &_sampleAcceptedDeviceCredentialsResponse[ 39 ], + .u.acceptedResponse.certificateIdLength = 6, + .u.acceptedResponse.pPrivateKey = ( const char * ) + &_sampleAcceptedDeviceCredentialsResponse[ 57 ], + .u.acceptedResponse.privateKeyLength = 7, + .u.acceptedResponse.pCertificateOwnershipToken = ( const char * ) + &_sampleAcceptedDeviceCredentialsResponse[ 92 ], + .u.acceptedResponse.ownershipTokenLength = 6, }; /*-----------------------------------------------------------*/ @@ -123,7 +132,6 @@ AwsIotOnboardingGetDeviceCredentialsResponse_t _expectedDeviceCredentialsParsedP static void _verifyParsedRejectedResponse( const AwsIotOnboardingRejectedResponse_t * pExpectedData, const AwsIotOnboardingRejectedResponse_t * pParsedData ); - /** * @brief Test user-callback to set expectations on parsing of #_sampleRejectedServerResponsePayload as rejected server * response. It will be passed as context parameter in callback parameter passed in tests. @@ -132,12 +140,19 @@ static void _testGetDeviceCredentialsRejectedCallback( void * contextParam, const AwsIotOnboardingGetDeviceCredentialsResponse_t * pResponseInfo ); /** - * @brief Test user-callback to set expectations on parsing of #_sampleRejectedServerResponsePayload as rejected server - * response. It will be passed as context parameter in callback parameter passed in tests. + * @brief Test user-callback to set expectations on parsing of #_sampleAcceptedDeviceCredentialsResponse as rejected + * server response. It will be passed as context parameter in callback parameter passed in tests. */ static void _testGetDeviceCredentialsAcceptedCallback( void * contextParam, const AwsIotOnboardingGetDeviceCredentialsResponse_t * pResponseInfo ); +/** + * @brief Callback for the device credentials parser that fails on being invoked. This is meant to be used for tests + * that DO NOT expect the callback to be invoked! + */ +static void _deviceCredentialsCallbackThatFailsOnInvokation( void * contextParam, + const AwsIotOnboardingGetDeviceCredentialsResponse_t * pResponseInfo ); + /** * @brief Test user-callback to set expectations on parsing of #_sampleRejectedServerResponsePayload as rejected server * response. It will be passed as context parameter in callback parameter passed in tests. @@ -200,8 +215,22 @@ static void _testGetDeviceCredentialsAcceptedCallback( void * contextParam, pResponseInfo->u.acceptedResponse.privateKeyLength ); AwsIotOnboarding_Assert( pExpectedParams->u.acceptedResponse.pPrivateKey == pResponseInfo->u.acceptedResponse.pPrivateKey ); + AwsIotOnboarding_Assert( pExpectedParams->u.acceptedResponse.ownershipTokenLength == + pResponseInfo->u.acceptedResponse.ownershipTokenLength ); + AwsIotOnboarding_Assert( pExpectedParams->u.acceptedResponse.pCertificateOwnershipToken == + pResponseInfo->u.acceptedResponse.pCertificateOwnershipToken ); } +/*-----------------------------------------------------------*/ + +static void _deviceCredentialsCallbackThatFailsOnInvokation( void * contextParam, + const AwsIotOnboardingGetDeviceCredentialsResponse_t * pResponseInfo ) +{ + ( void ) contextParam; + ( void ) pResponseInfo; + + AwsIotOnboarding_Assert( false ); +} /*-----------------------------------------------------------*/ @@ -258,13 +287,14 @@ TEST_GROUP_RUNNER( Onboarding_Unit_Parser ) { RUN_TEST_CASE( Onboarding_Unit_Parser, TestParseDeviceCredentialsRejectedResponse ); RUN_TEST_CASE( Onboarding_Unit_Parser, TestParseDeviceCredentialsAcceptedResponse ); + RUN_TEST_CASE( Onboarding_Unit_Parser, TestParseDeviceCredentialsResponseWithMissingEntries ); RUN_TEST_CASE( Onboarding_Unit_Parser, TestParseOnboardDeviceRejectedResponse ); } /*-----------------------------------------------------------*/ /** - * @brief Verifies the parser function behavior (_AwsIotOnboarding_ParseDeviceCredentialsResponse) when the + * @brief Verifies the parser function behavior @ref _AwsIotOnboarding_ParseDeviceCredentialsResponse when the * GetDeviceCredentials service API responds with a rejected payload. */ TEST( Onboarding_Unit_Parser, TestParseDeviceCredentialsRejectedResponse ) @@ -281,7 +311,7 @@ TEST( Onboarding_Unit_Parser, TestParseDeviceCredentialsRejectedResponse ) } /** - * @brief Verifies the parser function (_AwsIotOnboarding_ParseDeviceCredentialsResponse) can parse the device + * @brief Verifies the parser function @ref _AwsIotOnboarding_ParseDeviceCredentialsResponse can parse the device * credentials response sent by the server. */ TEST( Onboarding_Unit_Parser, TestParseDeviceCredentialsAcceptedResponse ) @@ -292,11 +322,89 @@ TEST( Onboarding_Unit_Parser, TestParseDeviceCredentialsAcceptedResponse ) wrapperCallback.getDeviceCredentialsCallback.function = _testGetDeviceCredentialsAcceptedCallback; TEST_ASSERT_EQUAL( AWS_IOT_ONBOARDING_SUCCESS, _AwsIotOnboarding_ParseDeviceCredentialsResponse( AWS_IOT_ACCEPTED, - _sampleDeviceCredentialsResponse, - sizeof( _sampleDeviceCredentialsResponse ), + _sampleAcceptedDeviceCredentialsResponse, + sizeof( _sampleAcceptedDeviceCredentialsResponse ), &wrapperCallback ) ); } +/** + * @brief Verifies that the parser function @ref _AwsIotOnboarding_ParseDeviceCredentialsResponse does not call the + * user-callback when the response payload has missing entries from the expected set of response data. + */ +TEST( Onboarding_Unit_Parser, TestParseDeviceCredentialsResponseWithMissingEntries ) +{ + _onboardingCallbackInfo_t wrapperCallback; + + wrapperCallback.getDeviceCredentialsCallback.userParam = NULL; + wrapperCallback.getDeviceCredentialsCallback.function = _testGetDeviceCredentialsAcceptedCallback; + + /*************** Response payload only with private key ********************/ + const uint8_t payloadWithOnlyPrivateKey[] = + { + 0xA2, /* # map( 1 ) */ + 0x6A, /* # text( 10 ) */ + 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4B, 0x65, 0x79, /* # "privateKey" */ + 0x4A, /* # bytes( 10 ) */ + 0x78, 0x9A, 0x78, 0x9A, 0x78, 0x9A, 0x78, 0x9A, 0x78, 0x9A, /* # "x\x9Ax\x9Ax\x9Ax\x9Ax\x9A" */ + }; + + TEST_ASSERT_EQUAL( AWS_IOT_ONBOARDING_BAD_RESPONSE, _AwsIotOnboarding_ParseDeviceCredentialsResponse( AWS_IOT_ACCEPTED, + payloadWithOnlyPrivateKey, + sizeof( payloadWithOnlyPrivateKey ), + &wrapperCallback ) ); + + /*************** Response payload only with certificate Pem entry********************/ + const uint8_t payloadWithOnlyCertificatePem[] = + { + 0xA1, /* # map( 1 ) */ + 0x6E, /* # text( 14 ) */ + 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x65, 0x6D, /* # "certificatePem" */ + 0x67, /* # text(7) */ + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* # "abcdefg" */ + }; + + + TEST_ASSERT_EQUAL( AWS_IOT_ONBOARDING_BAD_RESPONSE, _AwsIotOnboarding_ParseDeviceCredentialsResponse( AWS_IOT_ACCEPTED, + payloadWithOnlyCertificatePem, + sizeof( payloadWithOnlyCertificatePem ), + &wrapperCallback ) ); + + /*************** Response payload only with certificate ID entry********************/ + const uint8_t payloadWithOnlyCertificateId[] = + { + 0xA1, /* # map( 1 ) */ + 0x6D, /* # text(13) */ + 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x49, 0x64, /* # "certificateId" */ + 0x66, /* # text(6) */ + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, /* # "hijklm" */ + }; + + + TEST_ASSERT_EQUAL( AWS_IOT_ONBOARDING_BAD_RESPONSE, _AwsIotOnboarding_ParseDeviceCredentialsResponse( AWS_IOT_ACCEPTED, + payloadWithOnlyCertificateId, + sizeof( payloadWithOnlyCertificateId ), + &wrapperCallback ) ); + + /*************** Response payload only with ownership token entry********************/ + const uint8_t payloadWithOnlyToken[] = + { + 0xA1, /* # map( 1 ) */ + 0x78, 0x19, /*# text(25) */ + 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x4F, 0x77, 0x6E, + 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x54, 0x6F, 0x6B, 0x65, 0x6E, /*# "certificateOwnershipToken" + * */ + 0x66, /*# text(6) */ + 0x54, 0x6F, 0x6B, 0x65, 0x6E, 0x21 /*# "Token!" */ + }; + + + TEST_ASSERT_EQUAL( AWS_IOT_ONBOARDING_BAD_RESPONSE, _AwsIotOnboarding_ParseDeviceCredentialsResponse( AWS_IOT_ACCEPTED, + payloadWithOnlyToken, + sizeof( payloadWithOnlyToken ), + &wrapperCallback ) ); +} + + /** * @brief Verifies the parser function behavior (_AwsIotOnboarding_ParseOnboardDeviceResponse) when the * OnboardDevice service API responds with a rejected payload. diff --git a/libraries/aws/onboarding/test/unit/aws_iot_tests_onboarding_serializer.c b/libraries/aws/onboarding/test/unit/aws_iot_tests_onboarding_serializer.c index 5130fddcee..cc7302dee9 100644 --- a/libraries/aws/onboarding/test/unit/aws_iot_tests_onboarding_serializer.c +++ b/libraries/aws/onboarding/test/unit/aws_iot_tests_onboarding_serializer.c @@ -46,6 +46,11 @@ */ static const char * _testCertificateId = "TestCertificateId"; +/** + * @brief The token string to use for OnboardDevice Request serialization tests. + */ +static const char * _testCertificateToken = "Token!"; + /** * @brief A sample list of parameters entries for testing serialization logic. */ @@ -152,6 +157,8 @@ TEST( Onboarding_Unit_Serializer, TestOnboardDeviceSerializationNominalCase ) testRequestInfo.pDeviceCertificateId = _testCertificateId; testRequestInfo.deviceCertificateIdLength = strlen( _testCertificateId ); + testRequestInfo.pCertificateOwnershipToken = _testCertificateToken; + testRequestInfo.ownershipTokenLength = strlen( _testCertificateToken ); testRequestInfo.pParametersStart = _sampleParameters; testRequestInfo.numOfParameters = _numOfSampleParameters; @@ -164,13 +171,18 @@ TEST( Onboarding_Unit_Serializer, TestOnboardDeviceSerializationNominalCase ) static const uint8_t pExpectedSerialization[] = { /* *INDENT-OFF* */ - 0xA2, /*# map( 2 ) */ + 0xA3, /*# map( 2 ) */ 0x6D, /*# text( 13 ) */ 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x49, 0x64, /*# "certificateId" */ 0x71, /*# text( 17 ) */ 0x54, 0x65, 0x73, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x49, 0x64, /*# "TestCertificateId" */ - 0x6D, /*# text( 13 ) */ - 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, /*# "deviceContext" */ + 0x78, 0x19, /*# text(25) */ + 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x4F, 0x77, 0x6E, + 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x54, 0x6F, 0x6B, 0x65, 0x6E, /*# "certificateOwnershipToken" */ + 0x66, /*# text(6) */ + 0x54, 0x6F, 0x6B, 0x65, 0x6E, 0x21, /*# "Token!" */ + 0x6A, /*# text( 10 ) */ + 0x70,0x61,0x72,0x61,0x6D,0x65,0x74,0x65,0x72,0x73, /*# "parameters" */ 0xA3, /*# map(3) */ 0x66, /*# text(6) */ 0x50, 0x61, 0x72, 0x61, 0x6D, 0x31, /*# "Param1" */ @@ -222,6 +234,8 @@ TEST( Onboarding_Unit_Serializer, TestOnboardDeviceSerializationWithoutParameter testRequestInfo.pDeviceCertificateId = _testCertificateId; testRequestInfo.deviceCertificateIdLength = strlen( _testCertificateId ); + testRequestInfo.pCertificateOwnershipToken = _testCertificateToken; + testRequestInfo.ownershipTokenLength = strlen( _testCertificateToken ); testRequestInfo.pParametersStart = NULL; testRequestInfo.numOfParameters = 0; @@ -234,11 +248,16 @@ TEST( Onboarding_Unit_Serializer, TestOnboardDeviceSerializationWithoutParameter static const uint8_t pExpectedSerializationWithoutParameters[] = { /* *INDENT-OFF* */ - 0xA1, /*# map( 2 ) */ + 0xA2, /*# map( 2 ) */ 0x6D, /*# text( 13 ) */ 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x49, 0x64, /*# "certificateId" */ 0x71, /*# text( 17 ) */ 0x54, 0x65, 0x73, 0x74, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x49, 0x64, /*# "TestCertificateId" */ + 0x78, 0x19, /*# text(25) */ + 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x4F, 0x77, 0x6E, + 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x54, 0x6F, 0x6B, 0x65, 0x6E, /*# "certificateOwnershipToken" */ + 0x66, /*# text(6) */ + 0x54, 0x6F, 0x6B, 0x65, 0x6E, 0x21, /*# "Token!" */ /* *INDENT-ON* */ }; diff --git a/scripts/ci_test_onboarding.sh b/scripts/ci_test_onboarding.sh index 4387022fb8..0e630bdde8 100644 --- a/scripts/ci_test_onboarding.sh +++ b/scripts/ci_test_onboarding.sh @@ -67,19 +67,7 @@ configure_credentials() { configure_credentials -# Create unique certificate on the AWS IoT account that can be used as the certificate to provision -# the system/integration tests with. We will just save the certificate ID to use in the tests. -CERTIFICATE_ID=$(aws iot create-keys-and-certificate \ - --endpoint $AWS_CLI_CI_ENDPOINT \ - --region $AWS_PROVISIONING_REGION \ - --no-set-as-active | \ - grep certificateId | \ - cut -d ':' -f2 | \ - tr -d , | \ - tr -d ' ' | \ - tr -d \") - -COMMON_CMAKE_C_FLAGS="$AWS_IOT_CREDENTIAL_DEFINES -DAWS_IOT_TEST_ONBOARDING_TEMPLATE_NAME=\"\\\"$TEMPLATE_NAME\\\"\" -DAWS_IOT_TEST_ONBOARDING_TEMPLATE_PARAMETERS=\"$PROVISION_PARAMETERS\" -DAWS_IOT_TEST_PROVISIONING_CERTIFICATE_ID=\"\\\"$CERTIFICATE_ID\\\"\" -DAWS_IOT_TEST_PROVISIONING_CLIENT_ID=\"\\\"$CLIENT_ID\\\"\"" +COMMON_CMAKE_C_FLAGS="$AWS_IOT_CREDENTIAL_DEFINES -DAWS_IOT_TEST_ONBOARDING_TEMPLATE_NAME=\"\\\"$TEMPLATE_NAME\\\"\" -DAWS_IOT_TEST_ONBOARDING_TEMPLATE_PARAMETERS=\"$PROVISION_PARAMETERS\" -DAWS_IOT_TEST_PROVISIONING_CLIENT_ID=\"\\\"$CLIENT_ID\\\"\"" # CMake build configuration without static memory mode. cmake .. -DIOT_BUILD_TESTS=1 -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS="$COMMON_CMAKE_C_FLAGS" @@ -97,31 +85,58 @@ cmake .. -DIOT_BUILD_TESTS=1 -DCMAKE_BUILD_TYPE=Debug -DIOT_NETWORK_USE_OPENSSL= # Run tests in no static memory mode. run_tests -# Cleanup created resources from the AWS IoT account used for CI. +# Cleanup the created resources created by the integration tests on the CI AWS IoT account. +# (Resources include Thing resource, its attached certificates and their policies) +# (First, we will install a json parser utility, jq) +apt-get install -y jq + +# Iterate over all the principals/certificates attached to the Thing resource (created by the integration test) +# and delete the certificates. aws iot list-thing-principals \ --endpoint $AWS_CLI_CI_ENDPOINT \ --region $AWS_PROVISIONING_REGION \ --thing-name "ThingPrefix_"$CLIENT_ID | \ grep arn | tr -d \",' ' | - while read -r certificate_arn + while read -r CERTIFICATE_ARN do + # Detach the principal from the Thing resource. aws iot detach-thing-principal \ --endpoint $AWS_CLI_CI_ENDPOINT \ --region $AWS_PROVISIONING_REGION \ --thing-name "ThingPrefix_"$CLIENT_ID \ - --principal $certificate_arn + --principal $CERTIFICATE_ARN + + CERTIFICATE_ID=$(echo $CERTIFICATE_ARN | cut -d '/' -f2) + + aws iot update-certificate \ + --endpoint $AWS_CLI_CI_ENDPOINT \ + --region $AWS_PROVISIONING_REGION \ + --certificate-id $CERTIFICATE_ID \ + --new-status INACTIVE + + aws iot delete-certificate \ + --endpoint $AWS_CLI_CI_ENDPOINT \ + --region $AWS_PROVISIONING_REGION \ + --certificate-id $CERTIFICATE_ID \ + --force-delete done aws iot delete-thing \ --endpoint $AWS_CLI_CI_ENDPOINT \ --region $AWS_PROVISIONING_REGION \ --thing-name "ThingPrefix_"$CLIENT_ID -aws iot update-certificate \ - --endpoint $AWS_CLI_CI_ENDPOINT \ - --region $AWS_PROVISIONING_REGION \ - --certificate-id $CERTIFICATE_ID \ - --new-status INACTIVE -aws iot delete-certificate \ - --endpoint $AWS_CLI_CI_ENDPOINT \ - --region $AWS_PROVISIONING_REGION \ - --certificate-id $CERTIFICATE_ID \ - --force-delete \ No newline at end of file + +# Delete any inactive certificate that may have been created by the integration tests. +aws iot list-certificates \ + --endpoint https://gamma.us-east-1.iot.amazonaws.com \ + --region $AWS_PROVISIONING_REGION | \ + jq -c '.certificates[] | select(.status | contains("INACTIVE")) | .certificateArn' | \ + tr -d \" | \ + while read -r cert_arn + do + CERTIFICATE_ID=$(echo $cert_arn | cut -d '/' -f2) + aws iot delete-certificate \ + --endpoint https://gamma.us-east-1.iot.amazonaws.com \ + --region $AWS_PROVISIONING_REGION \ + --certificate-id $CERTIFICATE_ID \ + --force-delete + done \ No newline at end of file