Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
282 lines (221 sloc) 7.99 KB
/*
* Copyright (C) 2018 Orange
*
* This software is distributed under the terms and conditions of the 'BSD-3-Clause-Clear'
* license which can be found in the file 'LICENSE.txt' in this package distribution
* or at 'https://spdx.org/licenses/BSD-3-Clause-Clear.html'.
*/
/**
* @author Emile-Hugo Spir
*/
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <sys/param.h>
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
typedef unsigned int uint;
#include "../io_management.h"
#include <layout.h>
#include "network.h"
#include "userland.h"
#include "../validation.h"
#include "../core.h"
#include "../driver_api.h"
const uint8_t __attribute__((section(".rodata.Ravens.storageRoom"), aligned(BLOCK_SIZE))) updateStorage[16 * BLOCK_SIZE] = {0xff}; //65kB of room.
void sendManifestRequest()
{
char version[sizeof(criticalMetadata.versionID) * 3 + 1];
uint length = sizeof(BASE_REQUEST) - 1;
char request[sizeof(criticalMetadata.updateChallenge) * 2 + sizeof(version)
+ sizeof(BASE_REQUEST) + sizeof(SEC_REQUEST) + sizeof(FINAL_REQUEST)];
strcpy(request, BASE_REQUEST);
itoa(criticalMetadata.versionID, version, 10);
uint8_t versionLength = (uint8_t) strlen(version);
strcpy(&request[length], version);
length += versionLength;
strcpy(&request[length], SEC_REQUEST);
length += sizeof(SEC_REQUEST) - 1;
length += base64_encode((const uint8_t *) criticalMetadata.updateChallenge, sizeof(criticalMetadata.updateChallenge), &request[length]);
strcpy(&request[length], FINAL_REQUEST);
length += sizeof(FINAL_REQUEST);
proxyNewRequest(UPDATE_SERVER, UPDATE_SERVER_PORT, request, length);
}
void processValidationRequest(UpdateHashRequest * extraValidation, char ** response, uint32_t * responseLength)
{
//Check the signature
if(validateExtraValidation(extraValidation))
{
//We hash the various segments that were requested and append them to the response buffer
*response = malloc((size_t) (extraValidation->numberValidation * HASH_LENGTH) + 1);
if(*response != NULL)
{
for(uint16_t i = 0; i < extraValidation->numberValidation; ++i)
{
uint8_t hash[HASH_LENGTH];
hashMemory((const uint8_t *) (uintptr_t) extraValidation->validateSegment[i].start, extraValidation->validateSegment[i].length, hash);
memcpy(&(*response)[i * HASH_LENGTH], hash, sizeof(hash));
}
*responseLength = extraValidation->numberValidation * HASH_LENGTH;
(*response)[*responseLength] = 0;
}
}
}
void sendPayloadRequest(const char * validationString, uint32_t validationStringLength)
{
char version[sizeof(criticalMetadata.versionID) * 3 + 1];
itoa(criticalMetadata.versionID, version, 10);
uint8_t versionLength = (uint8_t) strlen(version);
uint length = sizeof(PAYL_REQUEST) - 1;
char request[sizeof(PAYL_REQUEST) + versionLength + sizeof(PAYL_SEC_REQUEST) + sizeof(version) + sizeof(FINAL_REQUEST) + validationStringLength + 1];
strcpy(request, PAYL_REQUEST);
strcpy(&request[length], version);
length += versionLength;
strcpy(&request[length], PAYL_SEC_REQUEST);
length += sizeof(PAYL_SEC_REQUEST) - 1;
itoa(validationStringLength, version, 10);
strcpy(&request[length], version);
length += strlen(version);
strcpy(&request[length], FINAL_REQUEST);
length += sizeof(FINAL_REQUEST) - 1;
memcpy(&request[length], validationString, validationStringLength);
length += validationStringLength;
request[length] = 0;
proxyNewRequest(UPDATE_SERVER, UPDATE_SERVER_PORT, request, length);
}
uint32_t getVersion()
{
return criticalMetadata.versionID;
}
void checkUpdate()
{
sendManifestRequest();
//Wait for data
uint8_t networkStatus;
uint32_t length = 0;
do
{
networkStatus = proxyRequestHasData();
if(networkStatus == NETWORK_NO_DATA)
{
usleep(50);
continue;
}
else if(networkStatus == NETWORK_HAS_DATA)
{
const NetworkStreamingData * data = proxyRequestGetData();
if(data == NULL)
return proxyCloseSession();
const uint32_t localLength = data->length;
if(data->buffer == NULL || localLength > sizeof(cacheRAM) - length)
return proxyCloseSession();
//Copy the reply to a safer buffer
memcpy(&cacheRAM[length], data->buffer, localLength);
length += localLength;
}
} while(networkStatus != NETWORK_FINISHED);
//Do we have something invalid?
//Get the data and verify we have at least a valid HTTP reply
if(length <= sizeof(EXPECTED_REPLY_UPDATE) || strncmp((const char *) cacheRAM, EXPECTED_REPLY_UPDATE, sizeof(EXPECTED_REPLY_UPDATE) - 1) != 0)
{
return proxyCloseSession();
}
proxyReleaseData();
for(uint16_t i = 0; i < length; ++i)
cacheRAM[i] = cacheRAM[sizeof(EXPECTED_REPLY_UPDATE) - 1 + i];
//Validate the header
UpdateHeader * header = (UpdateHeader *) cacheRAM;
if(!validateHeader(header))
return proxyCloseSession();
char * challengeResponse = NULL;
uint32_t challengeResponseLength = 0;
if(header->sectionSignedDeviceKey.haveExtra)
{
UpdateHashRequest * extraValidation = (UpdateHashRequest *) (cacheRAM + sizeof(UpdateHeader));
//We check the validation wasn't truncated
if(length - sizeof(UpdateHeader) >= extraValidation->numberValidation * sizeof(extraValidation->validateSegment[0]) + sizeof(extraValidation->numberValidation))
processValidationRequest(extraValidation, &challengeResponse, &challengeResponseLength);
}
//Send the request. Meanwhile, we start writing the header
sendPayloadRequest(challengeResponse, challengeResponseLength);
//Flush the space necessary to write the update
eraseNecessarySpace((const void *) updateStorage, sizeof(UpdateHeader) + header->sectionSignedDeviceKey.manifestLength);
//Write the update header
size_t writePosition = (size_t) updateStorage;
uint16_t httpReplyLeft = sizeof(EXPECTED_REPLY_UPDATE) - 1, bufferPos = sizeof(UpdateHeader);
size_t manifestLength = header->sectionSignedDeviceKey.manifestLength;
while((networkStatus = proxyRequestHasData()) != NETWORK_FINISHED)
{
if(networkStatus == NETWORK_HAS_DATA)
{
const NetworkStreamingData * data = proxyRequestGetData();
if(data == NULL || data->buffer == NULL || data->length == 0)
continue;
uint32_t dataLength = data->length, currentInputBufferPos = 0;
const uint8_t * buffer = data->buffer;
//Remove the HTTP reply
if(httpReplyLeft != 0)
{
if(dataLength >= httpReplyLeft)
{
if(strncmp((const char *) buffer, EXPECTED_REPLY_UPDATE + (sizeof(EXPECTED_REPLY_UPDATE) - 1 - httpReplyLeft), httpReplyLeft) != 0)
return proxyCloseSession();
dataLength -= httpReplyLeft;
buffer = &buffer[httpReplyLeft];
httpReplyLeft = 0;
if(dataLength == 0)
continue;
}
else
{
if(strncmp((const char *) buffer, EXPECTED_REPLY_UPDATE + (sizeof(EXPECTED_REPLY_UPDATE) - 1 - httpReplyLeft), dataLength) != 0)
return proxyCloseSession();
httpReplyLeft -= data->length;
continue;
}
}
if(dataLength > manifestLength)
break;
//Copy the input data in the cacheRAM buffer page by page so that writes are properly aligned
do
{
const uint32_t spaceLeftInInputBuffer = dataLength - currentInputBufferPos;
const uint16_t spaceLeftInCache = (uint16_t) (BLOCK_SIZE - bufferPos);
if(spaceLeftInInputBuffer < spaceLeftInCache)
{
memcpy(&cacheRAM[bufferPos], &buffer[currentInputBufferPos], dataLength);
bufferPos += dataLength;
currentInputBufferPos += spaceLeftInInputBuffer;
}
else
{
memcpy(&cacheRAM[bufferPos], &buffer[currentInputBufferPos], spaceLeftInCache);
erasePage(writePosition);
writeToNAND(writePosition, BLOCK_SIZE, cacheRAM);
writePosition += BLOCK_SIZE;
bufferPos = 0;
currentInputBufferPos += spaceLeftInCache;
}
} while(currentInputBufferPos < dataLength);
manifestLength -= dataLength;
}
else
usleep(50);
}
proxyCloseSession();
//Some data left in the cache to commit
if(bufferPos != 0)
{
erasePage(writePosition);
writeToNAND(writePosition, BLOCK_SIZE, cacheRAM);
}
//We downloaded the full payload
if(manifestLength == 0)
{
requestUpdate(updateStorage);
reboot();
}
}
You can’t perform that action at this time.