This repository was archived by the owner on Oct 20, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathuserland.c
281 lines (221 loc) · 7.98 KB
/
userland.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
/*
* 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.Hermes.storageRoom"), aligned(BLOCK_SIZE))) updateStorage[16 * BLOCK_SIZE] = {0xff}; //65kB of room.
void sendRequest()
{
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 sendManifest2Request(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(MAN2_REQUEST) - 1;
char request[sizeof(MAN2_REQUEST) + versionLength + sizeof(MAN2_SEC_REQUEST) + sizeof(version) + sizeof(FINAL_REQUEST) + validationStringLength + 1];
strcpy(request, MAN2_REQUEST);
strcpy(&request[length], version);
length += versionLength;
strcpy(&request[length], MAN2_SEC_REQUEST);
length += sizeof(MAN2_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()
{
sendRequest();
//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
sendManifest2Request(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();
}
}