1 /*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <assert.h>
18 #include <ctype.h>
19 #include <fcntl.h>
20 #include <limits.h>
21 #include <pthread.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <openssl/aes.h>
27 #include <openssl/hmac.h>
28
29 #include "FwdLockConv.h"
30 #include "FwdLockGlue.h"
31
32 #define TRUE 1
33 #define FALSE 0
34
35 #define INVALID_OFFSET ((off64_t)-1)
36
37 #define MAX_NUM_SESSIONS 32
38
39 #define OUTPUT_BUFFER_SIZE_INCREMENT 1024
40 #define READ_BUFFER_SIZE 1024
41
42 #define MAX_BOUNDARY_LENGTH 70
43 #define MAX_DELIMITER_LENGTH (MAX_BOUNDARY_LENGTH + 4)
44
45 #define STRING_LENGTH_INCREMENT 25
46
47 #define KEY_SIZE AES_BLOCK_SIZE
48 #define KEY_SIZE_IN_BITS (KEY_SIZE * 8)
49
50 #define SHA1_HASH_SIZE 20
51
52 #define FWD_LOCK_VERSION 0
53 #define FWD_LOCK_SUBFORMAT 0
54 #define USAGE_RESTRICTION_FLAGS 0
55 #define CONTENT_TYPE_LENGTH_POS 7
56 #define TOP_HEADER_SIZE 8
57
58 /**
59 * Data type for the parser states of the converter.
60 */
61 typedef enum FwdLockConv_ParserState {
62 FwdLockConv_ParserState_WantsOpenDelimiter,
63 FwdLockConv_ParserState_WantsMimeHeaders,
64 FwdLockConv_ParserState_WantsBinaryEncodedData,
65 FwdLockConv_ParserState_WantsBase64EncodedData,
66 FwdLockConv_ParserState_Done
67 } FwdLockConv_ParserState_t;
68
69 /**
70 * Data type for the scanner states of the converter.
71 */
72 typedef enum FwdLockConv_ScannerState {
73 FwdLockConv_ScannerState_WantsFirstDash,
74 FwdLockConv_ScannerState_WantsSecondDash,
75 FwdLockConv_ScannerState_WantsCR,
76 FwdLockConv_ScannerState_WantsLF,
77 FwdLockConv_ScannerState_WantsBoundary,
78 FwdLockConv_ScannerState_WantsBoundaryEnd,
79 FwdLockConv_ScannerState_WantsMimeHeaderNameStart,
80 FwdLockConv_ScannerState_WantsMimeHeaderName,
81 FwdLockConv_ScannerState_WantsMimeHeaderNameEnd,
82 FwdLockConv_ScannerState_WantsContentTypeStart,
83 FwdLockConv_ScannerState_WantsContentType,
84 FwdLockConv_ScannerState_WantsContentTransferEncodingStart,
85 FwdLockConv_ScannerState_Wants_A_OR_I,
86 FwdLockConv_ScannerState_Wants_N,
87 FwdLockConv_ScannerState_Wants_A,
88 FwdLockConv_ScannerState_Wants_R,
89 FwdLockConv_ScannerState_Wants_Y,
90 FwdLockConv_ScannerState_Wants_S,
91 FwdLockConv_ScannerState_Wants_E,
92 FwdLockConv_ScannerState_Wants_6,
93 FwdLockConv_ScannerState_Wants_4,
94 FwdLockConv_ScannerState_Wants_B,
95 FwdLockConv_ScannerState_Wants_I,
96 FwdLockConv_ScannerState_Wants_T,
97 FwdLockConv_ScannerState_WantsContentTransferEncodingEnd,
98 FwdLockConv_ScannerState_WantsMimeHeaderValueEnd,
99 FwdLockConv_ScannerState_WantsMimeHeadersEnd,
100 FwdLockConv_ScannerState_WantsByte1,
101 FwdLockConv_ScannerState_WantsByte1_AfterCRLF,
102 FwdLockConv_ScannerState_WantsByte2,
103 FwdLockConv_ScannerState_WantsByte3,
104 FwdLockConv_ScannerState_WantsByte4,
105 FwdLockConv_ScannerState_WantsPadding,
106 FwdLockConv_ScannerState_WantsWhitespace,
107 FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF,
108 FwdLockConv_ScannerState_WantsDelimiter
109 } FwdLockConv_ScannerState_t;
110
111 /**
112 * Data type for the content transfer encoding.
113 */
114 typedef enum FwdLockConv_ContentTransferEncoding {
115 FwdLockConv_ContentTransferEncoding_Undefined,
116 FwdLockConv_ContentTransferEncoding_Binary,
117 FwdLockConv_ContentTransferEncoding_Base64
118 } FwdLockConv_ContentTransferEncoding_t;
119
120 /**
121 * Data type for a dynamically growing string.
122 */
123 typedef struct FwdLockConv_String {
124 char *ptr;
125 size_t length;
126 size_t maxLength;
127 size_t lengthIncrement;
128 } FwdLockConv_String_t;
129
130 /**
131 * Data type for the per-file state information needed by the converter.
132 */
133 typedef struct FwdLockConv_Session {
134 FwdLockConv_ParserState_t parserState;
135 FwdLockConv_ScannerState_t scannerState;
136 FwdLockConv_ScannerState_t savedScannerState;
137 off64_t numCharsConsumed;
138 char delimiter[MAX_DELIMITER_LENGTH];
139 size_t delimiterLength;
140 size_t delimiterMatchPos;
141 FwdLockConv_String_t mimeHeaderName;
142 FwdLockConv_String_t contentType;
143 FwdLockConv_ContentTransferEncoding_t contentTransferEncoding;
144 unsigned char sessionKey[KEY_SIZE];
145 void *pEncryptedSessionKey;
146 size_t encryptedSessionKeyLength;
147 AES_KEY encryptionRoundKeys;
148 HMAC_CTX signingContext;
149 unsigned char topHeader[TOP_HEADER_SIZE];
150 unsigned char counter[AES_BLOCK_SIZE];
151 unsigned char keyStream[AES_BLOCK_SIZE];
152 int keyStreamIndex;
153 unsigned char ch;
154 size_t outputBufferSize;
155 size_t dataOffset;
156 size_t numDataBytes;
157 } FwdLockConv_Session_t;
158
159 static FwdLockConv_Session_t *sessionPtrs[MAX_NUM_SESSIONS] = { NULL };
160
161 static pthread_mutex_t sessionAcquisitionMutex = PTHREAD_MUTEX_INITIALIZER;
162
163 static const FwdLockConv_String_t nullString = { NULL, 0, 0, STRING_LENGTH_INCREMENT };
164
165 static const unsigned char topHeaderTemplate[] =
166 { 'F', 'W', 'L', 'K', FWD_LOCK_VERSION, FWD_LOCK_SUBFORMAT, USAGE_RESTRICTION_FLAGS };
167
168 static const char strContent[] = "content-";
169 static const char strType[] = "type";
170 static const char strTransferEncoding[] = "transfer-encoding";
171 static const char strTextPlain[] = "text/plain";
172 static const char strApplicationVndOmaDrmRightsXml[] = "application/vnd.oma.drm.rights+xml";
173 static const char strApplicationVndOmaDrmContent[] = "application/vnd.oma.drm.content";
174
175 static const size_t strlenContent = sizeof strContent - 1;
176 static const size_t strlenTextPlain = sizeof strTextPlain - 1;
177
178 static const signed char base64Values[] = {
179 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
180 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
181 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
182 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
183 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
184 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
185 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
186 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
187 };
188
189 /**
190 * Acquires an unused converter session.
191 *
192 * @return A session ID.
193 */
FwdLockConv_AcquireSession()194 static int FwdLockConv_AcquireSession() {
195 int sessionId = -1;
196 int i;
197 pthread_mutex_lock(&sessionAcquisitionMutex);
198 for (i = 0; i < MAX_NUM_SESSIONS; ++i) {
199 if (sessionPtrs[i] == NULL) {
200 sessionPtrs[i] = malloc(sizeof *sessionPtrs[i]);
201 if (sessionPtrs[i] != NULL) {
202 sessionId = i;
203 }
204 break;
205 }
206 }
207 pthread_mutex_unlock(&sessionAcquisitionMutex);
208 return sessionId;
209 }
210
211 /**
212 * Checks whether a session ID is in range and currently in use.
213 *
214 * @param[in] sessionID A session ID.
215 *
216 * @return A Boolean value indicating whether the session ID is in range and currently in use.
217 */
FwdLockConv_IsValidSession(int sessionId)218 static int FwdLockConv_IsValidSession(int sessionId) {
219 return 0 <= sessionId && sessionId < MAX_NUM_SESSIONS && sessionPtrs[sessionId] != NULL;
220 }
221
222 /**
223 * Releases a converter session.
224 *
225 * @param[in] sessionID A session ID.
226 */
FwdLockConv_ReleaseSession(int sessionId)227 static void FwdLockConv_ReleaseSession(int sessionId) {
228 pthread_mutex_lock(&sessionAcquisitionMutex);
229 assert(FwdLockConv_IsValidSession(sessionId));
230 memset(sessionPtrs[sessionId], 0, sizeof *sessionPtrs[sessionId]); // Zero out key data.
231 free(sessionPtrs[sessionId]);
232 sessionPtrs[sessionId] = NULL;
233 pthread_mutex_unlock(&sessionAcquisitionMutex);
234 }
235
236 /**
237 * Derives cryptographically independent keys for encryption and signing from the session key.
238 *
239 * @param[in,out] pSession A reference to a converter session.
240 *
241 * @return A status code.
242 */
FwdLockConv_DeriveKeys(FwdLockConv_Session_t * pSession)243 static int FwdLockConv_DeriveKeys(FwdLockConv_Session_t *pSession) {
244 FwdLockConv_Status_t status;
245 struct FwdLockConv_DeriveKeys_Data {
246 AES_KEY sessionRoundKeys;
247 unsigned char value[KEY_SIZE];
248 unsigned char key[KEY_SIZE];
249 };
250 const size_t kSize = sizeof(struct FwdLockConv_DeriveKeys_Data);
251 struct FwdLockConv_DeriveKeys_Data *pData = malloc(kSize);
252 if (pData == NULL) {
253 status = FwdLockConv_Status_OutOfMemory;
254 } else {
255 if (AES_set_encrypt_key(pSession->sessionKey, KEY_SIZE_IN_BITS,
256 &pData->sessionRoundKeys) != 0) {
257 status = FwdLockConv_Status_ProgramError;
258 } else {
259 // Encrypt the 16-byte value {0, 0, ..., 0} to produce the encryption key.
260 memset(pData->value, 0, KEY_SIZE);
261 AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys);
262 if (AES_set_encrypt_key(pData->key, KEY_SIZE_IN_BITS,
263 &pSession->encryptionRoundKeys) != 0) {
264 status = FwdLockConv_Status_ProgramError;
265 } else {
266 // Encrypt the 16-byte value {1, 0, ..., 0} to produce the signing key.
267 ++pData->value[0];
268 AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys);
269 HMAC_CTX_init(&pSession->signingContext);
270 HMAC_Init_ex(&pSession->signingContext, pData->key, KEY_SIZE, EVP_sha1(), NULL);
271 status = FwdLockConv_Status_OK;
272 }
273 }
274 memset(pData, 0, kSize); // Zero out key data.
275 free(pData);
276 }
277 return status;
278 }
279
280 /**
281 * Checks whether a given character is valid in a boundary. Allows some non-standard characters that
282 * are invalid according to RFC 2046 but nevertheless used by one vendor's DRM packager. Note that
283 * the boundary may contain leading and internal spaces.
284 *
285 * @param[in] ch The character to check.
286 *
287 * @return A Boolean value indicating whether the given character is valid in a boundary.
288 */
FwdLockConv_IsBoundaryChar(int ch)289 static int FwdLockConv_IsBoundaryChar(int ch) {
290 return isalnum(ch) || ch == '\'' || ch == '(' || ch == ')' || ch == '+' || ch == '_' ||
291 ch == ',' || ch == '-' || ch == '.' || ch == '/' || ch == ':' || ch == '=' ||
292 ch == '?' || ch == ' ' || ch == '%' || ch == '[' || ch == '&' || ch == '*' || ch == '^';
293 }
294
295 /**
296 * Checks whether a given character should be considered whitespace, using a narrower definition
297 * than the standard-library isspace() function.
298 *
299 * @param[in] ch The character to check.
300 *
301 * @return A Boolean value indicating whether the given character should be considered whitespace.
302 */
FwdLockConv_IsWhitespace(int ch)303 static int FwdLockConv_IsWhitespace(int ch) {
304 return ch == ' ' || ch == '\t';
305 }
306
307 /**
308 * Removes trailing spaces from the delimiter.
309 *
310 * @param[in,out] pSession A reference to a converter session.
311 *
312 * @return A status code.
313 */
FwdLockConv_RightTrimDelimiter(FwdLockConv_Session_t * pSession)314 static FwdLockConv_Status_t FwdLockConv_RightTrimDelimiter(FwdLockConv_Session_t *pSession) {
315 while (pSession->delimiterLength > 4 &&
316 pSession->delimiter[pSession->delimiterLength - 1] == ' ') {
317 --pSession->delimiterLength;
318 }
319 if (pSession->delimiterLength > 4) {
320 return FwdLockConv_Status_OK;
321 }
322 return FwdLockConv_Status_SyntaxError;
323 }
324
325 /**
326 * Matches the open delimiter.
327 *
328 * @param[in,out] pSession A reference to a converter session.
329 * @param[in] ch A character.
330 *
331 * @return A status code.
332 */
FwdLockConv_MatchOpenDelimiter(FwdLockConv_Session_t * pSession,int ch)333 static FwdLockConv_Status_t FwdLockConv_MatchOpenDelimiter(FwdLockConv_Session_t *pSession,
334 int ch) {
335 FwdLockConv_Status_t status = FwdLockConv_Status_OK;
336 switch (pSession->scannerState) {
337 case FwdLockConv_ScannerState_WantsFirstDash:
338 if (ch == '-') {
339 pSession->scannerState = FwdLockConv_ScannerState_WantsSecondDash;
340 } else if (ch == '\r') {
341 pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
342 } else {
343 pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
344 }
345 break;
346 case FwdLockConv_ScannerState_WantsSecondDash:
347 if (ch == '-') {
348 // The delimiter starts with "\r\n--" (the open delimiter may omit the initial "\r\n").
349 // The rest is the user-defined boundary that should come next.
350 pSession->delimiter[0] = '\r';
351 pSession->delimiter[1] = '\n';
352 pSession->delimiter[2] = '-';
353 pSession->delimiter[3] = '-';
354 pSession->delimiterLength = 4;
355 pSession->scannerState = FwdLockConv_ScannerState_WantsBoundary;
356 } else if (ch == '\r') {
357 pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
358 } else {
359 pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
360 }
361 break;
362 case FwdLockConv_ScannerState_WantsCR:
363 if (ch == '\r') {
364 pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
365 }
366 break;
367 case FwdLockConv_ScannerState_WantsLF:
368 if (ch == '\n') {
369 pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash;
370 } else if (ch != '\r') {
371 pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
372 }
373 break;
374 case FwdLockConv_ScannerState_WantsBoundary:
375 if (FwdLockConv_IsBoundaryChar(ch)) {
376 // The boundary may contain leading and internal spaces, so trailing spaces will also be
377 // matched here. These will be removed later.
378 if (pSession->delimiterLength < MAX_DELIMITER_LENGTH) {
379 pSession->delimiter[pSession->delimiterLength++] = ch;
380 } else if (ch != ' ') {
381 status = FwdLockConv_Status_SyntaxError;
382 }
383 } else if (ch == '\r') {
384 status = FwdLockConv_RightTrimDelimiter(pSession);
385 if (status == FwdLockConv_Status_OK) {
386 pSession->scannerState = FwdLockConv_ScannerState_WantsBoundaryEnd;
387 }
388 } else if (ch == '\t') {
389 status = FwdLockConv_RightTrimDelimiter(pSession);
390 if (status == FwdLockConv_Status_OK) {
391 pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace;
392 }
393 } else {
394 status = FwdLockConv_Status_SyntaxError;
395 }
396 break;
397 case FwdLockConv_ScannerState_WantsWhitespace:
398 if (ch == '\r') {
399 pSession->scannerState = FwdLockConv_ScannerState_WantsBoundaryEnd;
400 } else if (!FwdLockConv_IsWhitespace(ch)) {
401 status = FwdLockConv_Status_SyntaxError;
402 }
403 break;
404 case FwdLockConv_ScannerState_WantsBoundaryEnd:
405 if (ch == '\n') {
406 pSession->parserState = FwdLockConv_ParserState_WantsMimeHeaders;
407 pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderNameStart;
408 } else {
409 status = FwdLockConv_Status_SyntaxError;
410 }
411 break;
412 default:
413 status = FwdLockConv_Status_ProgramError;
414 break;
415 }
416 return status;
417 }
418
419 /**
420 * Checks whether a given character is valid in a MIME header name.
421 *
422 * @param[in] ch The character to check.
423 *
424 * @return A Boolean value indicating whether the given character is valid in a MIME header name.
425 */
FwdLockConv_IsMimeHeaderNameChar(int ch)426 static int FwdLockConv_IsMimeHeaderNameChar(int ch) {
427 return isgraph(ch) && ch != ':';
428 }
429
430 /**
431 * Checks whether a given character is valid in a MIME header value.
432 *
433 * @param[in] ch The character to check.
434 *
435 * @return A Boolean value indicating whether the given character is valid in a MIME header value.
436 */
FwdLockConv_IsMimeHeaderValueChar(int ch)437 static int FwdLockConv_IsMimeHeaderValueChar(int ch) {
438 return isgraph(ch) && ch != ';';
439 }
440
441 /**
442 * Appends a character to the specified dynamically growing string.
443 *
444 * @param[in,out] pString A reference to a dynamically growing string.
445 * @param[in] ch The character to append.
446 *
447 * @return A status code.
448 */
FwdLockConv_StringAppend(FwdLockConv_String_t * pString,int ch)449 static FwdLockConv_Status_t FwdLockConv_StringAppend(FwdLockConv_String_t *pString, int ch) {
450 if (pString->length == pString->maxLength) {
451 size_t newMaxLength = pString->maxLength + pString->lengthIncrement;
452 char *newPtr = realloc(pString->ptr, newMaxLength + 1);
453 if (newPtr == NULL) {
454 return FwdLockConv_Status_OutOfMemory;
455 }
456 pString->ptr = newPtr;
457 pString->maxLength = newMaxLength;
458 }
459 pString->ptr[pString->length++] = ch;
460 pString->ptr[pString->length] = '\0';
461 return FwdLockConv_Status_OK;
462 }
463
464 /**
465 * Attempts to recognize the MIME header name and changes the scanner state accordingly.
466 *
467 * @param[in,out] pSession A reference to a converter session.
468 *
469 * @return A status code.
470 */
FwdLockConv_RecognizeMimeHeaderName(FwdLockConv_Session_t * pSession)471 static FwdLockConv_Status_t FwdLockConv_RecognizeMimeHeaderName(FwdLockConv_Session_t *pSession) {
472 FwdLockConv_Status_t status = FwdLockConv_Status_OK;
473 if (strncmp(pSession->mimeHeaderName.ptr, strContent, strlenContent) == 0) {
474 if (strcmp(pSession->mimeHeaderName.ptr + strlenContent, strType) == 0) {
475 if (pSession->contentType.ptr == NULL) {
476 pSession->scannerState = FwdLockConv_ScannerState_WantsContentTypeStart;
477 } else {
478 status = FwdLockConv_Status_SyntaxError;
479 }
480 } else if (strcmp(pSession->mimeHeaderName.ptr + strlenContent, strTransferEncoding) == 0) {
481 if (pSession->contentTransferEncoding ==
482 FwdLockConv_ContentTransferEncoding_Undefined) {
483 pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingStart;
484 } else {
485 status = FwdLockConv_Status_SyntaxError;
486 }
487 } else {
488 pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
489 }
490 } else {
491 pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
492 }
493 return status;
494 }
495
496 /**
497 * Applies defaults to missing MIME header values.
498 *
499 * @param[in,out] pSession A reference to a converter session.
500 *
501 * @return A status code.
502 */
FwdLockConv_ApplyDefaults(FwdLockConv_Session_t * pSession)503 static FwdLockConv_Status_t FwdLockConv_ApplyDefaults(FwdLockConv_Session_t *pSession) {
504 if (pSession->contentType.ptr == NULL) {
505 // Content type is missing: default to "text/plain".
506 pSession->contentType.ptr = malloc(sizeof strTextPlain);
507 if (pSession->contentType.ptr == NULL) {
508 return FwdLockConv_Status_OutOfMemory;
509 }
510 memcpy(pSession->contentType.ptr, strTextPlain, sizeof strTextPlain);
511 pSession->contentType.length = strlenTextPlain;
512 pSession->contentType.maxLength = strlenTextPlain;
513 }
514 if (pSession->contentTransferEncoding == FwdLockConv_ContentTransferEncoding_Undefined) {
515 // Content transfer encoding is missing: default to binary.
516 pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Binary;
517 }
518 return FwdLockConv_Status_OK;
519 }
520
521 /**
522 * Verifies that the content type is supported.
523 *
524 * @param[in,out] pSession A reference to a converter session.
525 *
526 * @return A status code.
527 */
FwdLockConv_VerifyContentType(FwdLockConv_Session_t * pSession)528 static FwdLockConv_Status_t FwdLockConv_VerifyContentType(FwdLockConv_Session_t *pSession) {
529 FwdLockConv_Status_t status;
530 if (pSession->contentType.ptr == NULL) {
531 status = FwdLockConv_Status_ProgramError;
532 } else if (strcmp(pSession->contentType.ptr, strApplicationVndOmaDrmRightsXml) == 0 ||
533 strcmp(pSession->contentType.ptr, strApplicationVndOmaDrmContent) == 0) {
534 status = FwdLockConv_Status_UnsupportedFileFormat;
535 } else {
536 status = FwdLockConv_Status_OK;
537 }
538 return status;
539 }
540
541 /**
542 * Writes the header of the output file.
543 *
544 * @param[in,out] pSession A reference to a converter session.
545 * @param[out] pOutput The output from the conversion process.
546 *
547 * @return A status code.
548 */
FwdLockConv_WriteHeader(FwdLockConv_Session_t * pSession,FwdLockConv_Output_t * pOutput)549 static FwdLockConv_Status_t FwdLockConv_WriteHeader(FwdLockConv_Session_t *pSession,
550 FwdLockConv_Output_t *pOutput) {
551 FwdLockConv_Status_t status;
552 if (pSession->contentType.length > UCHAR_MAX) {
553 status = FwdLockConv_Status_SyntaxError;
554 } else {
555 pSession->outputBufferSize = OUTPUT_BUFFER_SIZE_INCREMENT;
556 pOutput->fromConvertData.pBuffer = malloc(pSession->outputBufferSize);
557 if (pOutput->fromConvertData.pBuffer == NULL) {
558 status = FwdLockConv_Status_OutOfMemory;
559 } else {
560 size_t encryptedSessionKeyPos = TOP_HEADER_SIZE + pSession->contentType.length;
561 size_t dataSignaturePos = encryptedSessionKeyPos + pSession->encryptedSessionKeyLength;
562 size_t headerSignaturePos = dataSignaturePos + SHA1_HASH_SIZE;
563 pSession->dataOffset = headerSignaturePos + SHA1_HASH_SIZE;
564 memcpy(pSession->topHeader, topHeaderTemplate, sizeof topHeaderTemplate);
565 pSession->topHeader[CONTENT_TYPE_LENGTH_POS] =
566 (unsigned char)pSession->contentType.length;
567 memcpy(pOutput->fromConvertData.pBuffer, pSession->topHeader, TOP_HEADER_SIZE);
568 memcpy((char *)pOutput->fromConvertData.pBuffer + TOP_HEADER_SIZE,
569 pSession->contentType.ptr, pSession->contentType.length);
570 memcpy((char *)pOutput->fromConvertData.pBuffer + encryptedSessionKeyPos,
571 pSession->pEncryptedSessionKey, pSession->encryptedSessionKeyLength);
572
573 // Set the signatures to all zeros for now; they will have to be updated later.
574 memset((char *)pOutput->fromConvertData.pBuffer + dataSignaturePos, 0,
575 SHA1_HASH_SIZE);
576 memset((char *)pOutput->fromConvertData.pBuffer + headerSignaturePos, 0,
577 SHA1_HASH_SIZE);
578
579 pOutput->fromConvertData.numBytes = pSession->dataOffset;
580 status = FwdLockConv_Status_OK;
581 }
582 }
583 return status;
584 }
585
586 /**
587 * Matches the MIME headers.
588 *
589 * @param[in,out] pSession A reference to a converter session.
590 * @param[in] ch A character.
591 * @param[out] pOutput The output from the conversion process.
592 *
593 * @return A status code.
594 */
FwdLockConv_MatchMimeHeaders(FwdLockConv_Session_t * pSession,int ch,FwdLockConv_Output_t * pOutput)595 static FwdLockConv_Status_t FwdLockConv_MatchMimeHeaders(FwdLockConv_Session_t *pSession,
596 int ch,
597 FwdLockConv_Output_t *pOutput) {
598 FwdLockConv_Status_t status = FwdLockConv_Status_OK;
599 switch (pSession->scannerState) {
600 case FwdLockConv_ScannerState_WantsMimeHeaderNameStart:
601 if (FwdLockConv_IsMimeHeaderNameChar(ch)) {
602 pSession->mimeHeaderName.length = 0;
603 status = FwdLockConv_StringAppend(&pSession->mimeHeaderName, tolower(ch));
604 if (status == FwdLockConv_Status_OK) {
605 pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderName;
606 }
607 } else if (ch == '\r') {
608 pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeadersEnd;
609 } else if (!FwdLockConv_IsWhitespace(ch)) {
610 status = FwdLockConv_Status_SyntaxError;
611 }
612 break;
613 case FwdLockConv_ScannerState_WantsMimeHeaderName:
614 if (FwdLockConv_IsMimeHeaderNameChar(ch)) {
615 status = FwdLockConv_StringAppend(&pSession->mimeHeaderName, tolower(ch));
616 } else if (ch == ':') {
617 status = FwdLockConv_RecognizeMimeHeaderName(pSession);
618 } else if (FwdLockConv_IsWhitespace(ch)) {
619 pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderNameEnd;
620 } else {
621 status = FwdLockConv_Status_SyntaxError;
622 }
623 break;
624 case FwdLockConv_ScannerState_WantsMimeHeaderNameEnd:
625 if (ch == ':') {
626 status = FwdLockConv_RecognizeMimeHeaderName(pSession);
627 } else if (!FwdLockConv_IsWhitespace(ch)) {
628 status = FwdLockConv_Status_SyntaxError;
629 }
630 break;
631 case FwdLockConv_ScannerState_WantsContentTypeStart:
632 if (FwdLockConv_IsMimeHeaderValueChar(ch)) {
633 status = FwdLockConv_StringAppend(&pSession->contentType, tolower(ch));
634 if (status == FwdLockConv_Status_OK) {
635 pSession->scannerState = FwdLockConv_ScannerState_WantsContentType;
636 }
637 } else if (!FwdLockConv_IsWhitespace(ch)) {
638 status = FwdLockConv_Status_SyntaxError;
639 }
640 break;
641 case FwdLockConv_ScannerState_WantsContentType:
642 if (FwdLockConv_IsMimeHeaderValueChar(ch)) {
643 status = FwdLockConv_StringAppend(&pSession->contentType, tolower(ch));
644 } else if (ch == ';') {
645 pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
646 } else if (ch == '\r') {
647 pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
648 } else if (FwdLockConv_IsWhitespace(ch)) {
649 pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderValueEnd;
650 } else {
651 status = FwdLockConv_Status_SyntaxError;
652 }
653 break;
654 case FwdLockConv_ScannerState_WantsContentTransferEncodingStart:
655 if (ch == 'b' || ch == 'B') {
656 pSession->scannerState = FwdLockConv_ScannerState_Wants_A_OR_I;
657 } else if (ch == '7' || ch == '8') {
658 pSession->scannerState = FwdLockConv_ScannerState_Wants_B;
659 } else if (!FwdLockConv_IsWhitespace(ch)) {
660 status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
661 }
662 break;
663 case FwdLockConv_ScannerState_Wants_A_OR_I:
664 if (ch == 'i' || ch == 'I') {
665 pSession->scannerState = FwdLockConv_ScannerState_Wants_N;
666 } else if (ch == 'a' || ch == 'A') {
667 pSession->scannerState = FwdLockConv_ScannerState_Wants_S;
668 } else {
669 status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
670 }
671 break;
672 case FwdLockConv_ScannerState_Wants_N:
673 if (ch == 'n' || ch == 'N') {
674 pSession->scannerState = FwdLockConv_ScannerState_Wants_A;
675 } else {
676 status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
677 }
678 break;
679 case FwdLockConv_ScannerState_Wants_A:
680 if (ch == 'a' || ch == 'A') {
681 pSession->scannerState = FwdLockConv_ScannerState_Wants_R;
682 } else {
683 status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
684 }
685 break;
686 case FwdLockConv_ScannerState_Wants_R:
687 if (ch == 'r' || ch == 'R') {
688 pSession->scannerState = FwdLockConv_ScannerState_Wants_Y;
689 } else {
690 status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
691 }
692 break;
693 case FwdLockConv_ScannerState_Wants_Y:
694 if (ch == 'y' || ch == 'Y') {
695 pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Binary;
696 pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingEnd;
697 } else {
698 status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
699 }
700 break;
701 case FwdLockConv_ScannerState_Wants_S:
702 if (ch == 's' || ch == 'S') {
703 pSession->scannerState = FwdLockConv_ScannerState_Wants_E;
704 } else {
705 status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
706 }
707 break;
708 case FwdLockConv_ScannerState_Wants_E:
709 if (ch == 'e' || ch == 'E') {
710 pSession->scannerState = FwdLockConv_ScannerState_Wants_6;
711 } else {
712 status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
713 }
714 break;
715 case FwdLockConv_ScannerState_Wants_6:
716 if (ch == '6') {
717 pSession->scannerState = FwdLockConv_ScannerState_Wants_4;
718 } else {
719 status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
720 }
721 break;
722 case FwdLockConv_ScannerState_Wants_4:
723 if (ch == '4') {
724 pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Base64;
725 pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingEnd;
726 } else {
727 status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
728 }
729 break;
730 case FwdLockConv_ScannerState_Wants_B:
731 if (ch == 'b' || ch == 'B') {
732 pSession->scannerState = FwdLockConv_ScannerState_Wants_I;
733 } else {
734 status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
735 }
736 break;
737 case FwdLockConv_ScannerState_Wants_I:
738 if (ch == 'i' || ch == 'I') {
739 pSession->scannerState = FwdLockConv_ScannerState_Wants_T;
740 } else {
741 status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
742 }
743 break;
744 case FwdLockConv_ScannerState_Wants_T:
745 if (ch == 't' || ch == 'T') {
746 pSession->contentTransferEncoding = FwdLockConv_ContentTransferEncoding_Binary;
747 pSession->scannerState = FwdLockConv_ScannerState_WantsContentTransferEncodingEnd;
748 } else {
749 status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
750 }
751 break;
752 case FwdLockConv_ScannerState_WantsContentTransferEncodingEnd:
753 if (ch == ';') {
754 pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
755 } else if (ch == '\r') {
756 pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
757 } else if (FwdLockConv_IsWhitespace(ch)) {
758 pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderValueEnd;
759 } else {
760 status = FwdLockConv_Status_UnsupportedContentTransferEncoding;
761 }
762 break;
763 case FwdLockConv_ScannerState_WantsMimeHeaderValueEnd:
764 if (ch == ';') {
765 pSession->scannerState = FwdLockConv_ScannerState_WantsCR;
766 } else if (ch == '\r') {
767 pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
768 } else if (!FwdLockConv_IsWhitespace(ch)) {
769 status = FwdLockConv_Status_SyntaxError;
770 }
771 break;
772 case FwdLockConv_ScannerState_WantsCR:
773 if (ch == '\r') {
774 pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
775 }
776 break;
777 case FwdLockConv_ScannerState_WantsLF:
778 if (ch == '\n') {
779 pSession->scannerState = FwdLockConv_ScannerState_WantsMimeHeaderNameStart;
780 } else {
781 status = FwdLockConv_Status_SyntaxError;
782 }
783 break;
784 case FwdLockConv_ScannerState_WantsMimeHeadersEnd:
785 if (ch == '\n') {
786 status = FwdLockConv_ApplyDefaults(pSession);
787 if (status == FwdLockConv_Status_OK) {
788 status = FwdLockConv_VerifyContentType(pSession);
789 }
790 if (status == FwdLockConv_Status_OK) {
791 status = FwdLockConv_WriteHeader(pSession, pOutput);
792 }
793 if (status == FwdLockConv_Status_OK) {
794 if (pSession->contentTransferEncoding ==
795 FwdLockConv_ContentTransferEncoding_Binary) {
796 pSession->parserState = FwdLockConv_ParserState_WantsBinaryEncodedData;
797 } else {
798 pSession->parserState = FwdLockConv_ParserState_WantsBase64EncodedData;
799 }
800 pSession->scannerState = FwdLockConv_ScannerState_WantsByte1;
801 }
802 } else {
803 status = FwdLockConv_Status_SyntaxError;
804 }
805 break;
806 default:
807 status = FwdLockConv_Status_ProgramError;
808 break;
809 }
810 return status;
811 }
812
813 /**
814 * Increments the counter, treated as a 16-byte little-endian number, by one.
815 *
816 * @param[in,out] pSession A reference to a converter session.
817 */
FwdLockConv_IncrementCounter(FwdLockConv_Session_t * pSession)818 static void FwdLockConv_IncrementCounter(FwdLockConv_Session_t *pSession) {
819 size_t i = 0;
820 while ((++pSession->counter[i] == 0) && (++i < AES_BLOCK_SIZE))
821 ;
822 }
823
824 /**
825 * Encrypts the given character and writes it to the output buffer.
826 *
827 * @param[in,out] pSession A reference to a converter session.
828 * @param[in] ch The character to encrypt and write.
829 * @param[in,out] pOutput The output from the conversion process.
830 *
831 * @return A status code.
832 */
FwdLockConv_WriteEncryptedChar(FwdLockConv_Session_t * pSession,unsigned char ch,FwdLockConv_Output_t * pOutput)833 static FwdLockConv_Status_t FwdLockConv_WriteEncryptedChar(FwdLockConv_Session_t *pSession,
834 unsigned char ch,
835 FwdLockConv_Output_t *pOutput) {
836 if (pOutput->fromConvertData.numBytes == pSession->outputBufferSize) {
837 void *pBuffer;
838 pSession->outputBufferSize += OUTPUT_BUFFER_SIZE_INCREMENT;
839 pBuffer = realloc(pOutput->fromConvertData.pBuffer, pSession->outputBufferSize);
840 if (pBuffer == NULL) {
841 return FwdLockConv_Status_OutOfMemory;
842 }
843 pOutput->fromConvertData.pBuffer = pBuffer;
844 }
845 if (++pSession->keyStreamIndex == AES_BLOCK_SIZE) {
846 FwdLockConv_IncrementCounter(pSession);
847 pSession->keyStreamIndex = 0;
848 }
849 if (pSession->keyStreamIndex == 0) {
850 AES_encrypt(pSession->counter, pSession->keyStream, &pSession->encryptionRoundKeys);
851 }
852 ch ^= pSession->keyStream[pSession->keyStreamIndex];
853 ((unsigned char *)pOutput->fromConvertData.pBuffer)[pOutput->fromConvertData.numBytes++] = ch;
854 ++pSession->numDataBytes;
855 return FwdLockConv_Status_OK;
856 }
857
858 /**
859 * Matches binary-encoded content data and encrypts it, while looking out for the close delimiter.
860 *
861 * @param[in,out] pSession A reference to a converter session.
862 * @param[in] ch A character.
863 * @param[in,out] pOutput The output from the conversion process.
864 *
865 * @return A status code.
866 */
FwdLockConv_MatchBinaryEncodedData(FwdLockConv_Session_t * pSession,int ch,FwdLockConv_Output_t * pOutput)867 static FwdLockConv_Status_t FwdLockConv_MatchBinaryEncodedData(FwdLockConv_Session_t *pSession,
868 int ch,
869 FwdLockConv_Output_t *pOutput) {
870 FwdLockConv_Status_t status = FwdLockConv_Status_OK;
871 switch (pSession->scannerState) {
872 case FwdLockConv_ScannerState_WantsByte1:
873 if (ch != pSession->delimiter[pSession->delimiterMatchPos]) {
874 // The partial match of the delimiter turned out to be spurious. Flush the matched bytes
875 // to the output buffer and start over.
876 size_t i;
877 for (i = 0; i < pSession->delimiterMatchPos; ++i) {
878 status = FwdLockConv_WriteEncryptedChar(pSession, pSession->delimiter[i], pOutput);
879 if (status != FwdLockConv_Status_OK) {
880 return status;
881 }
882 }
883 pSession->delimiterMatchPos = 0;
884 }
885 if (ch != pSession->delimiter[pSession->delimiterMatchPos]) {
886 // The current character isn't part of the delimiter. Write it to the output buffer.
887 status = FwdLockConv_WriteEncryptedChar(pSession, ch, pOutput);
888 } else if (++pSession->delimiterMatchPos == pSession->delimiterLength) {
889 // The entire delimiter has been matched. The only valid characters now are the "--"
890 // that complete the close delimiter (no more message parts are expected).
891 pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash;
892 }
893 break;
894 case FwdLockConv_ScannerState_WantsFirstDash:
895 if (ch == '-') {
896 pSession->scannerState = FwdLockConv_ScannerState_WantsSecondDash;
897 } else {
898 status = FwdLockConv_Status_SyntaxError;
899 }
900 break;
901 case FwdLockConv_ScannerState_WantsSecondDash:
902 if (ch == '-') {
903 pSession->parserState = FwdLockConv_ParserState_Done;
904 } else {
905 status = FwdLockConv_Status_SyntaxError;
906 }
907 break;
908 default:
909 status = FwdLockConv_Status_ProgramError;
910 break;
911 }
912 return status;
913 }
914
915 /**
916 * Checks whether a given character is valid in base64-encoded data.
917 *
918 * @param[in] ch The character to check.
919 *
920 * @return A Boolean value indicating whether the given character is valid in base64-encoded data.
921 */
FwdLockConv_IsBase64Char(int ch)922 static int FwdLockConv_IsBase64Char(int ch) {
923 return 0 <= ch && ch <= 'z' && base64Values[ch] >= 0;
924 }
925
926 /**
927 * Matches base64-encoded content data and encrypts it, while looking out for the close delimiter.
928 *
929 * @param[in,out] pSession A reference to a converter session.
930 * @param[in] ch A character.
931 * @param[in,out] pOutput The output from the conversion process.
932 *
933 * @return A status code.
934 */
FwdLockConv_MatchBase64EncodedData(FwdLockConv_Session_t * pSession,int ch,FwdLockConv_Output_t * pOutput)935 static FwdLockConv_Status_t FwdLockConv_MatchBase64EncodedData(FwdLockConv_Session_t *pSession,
936 int ch,
937 FwdLockConv_Output_t *pOutput) {
938 FwdLockConv_Status_t status = FwdLockConv_Status_OK;
939 switch (pSession->scannerState) {
940 case FwdLockConv_ScannerState_WantsByte1:
941 case FwdLockConv_ScannerState_WantsByte1_AfterCRLF:
942 if (FwdLockConv_IsBase64Char(ch)) {
943 pSession->ch = base64Values[ch] << 2;
944 pSession->scannerState = FwdLockConv_ScannerState_WantsByte2;
945 } else if (ch == '\r') {
946 pSession->savedScannerState = FwdLockConv_ScannerState_WantsByte1_AfterCRLF;
947 pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
948 } else if (ch == '-') {
949 if (pSession->scannerState == FwdLockConv_ScannerState_WantsByte1_AfterCRLF) {
950 pSession->delimiterMatchPos = 3;
951 pSession->scannerState = FwdLockConv_ScannerState_WantsDelimiter;
952 } else {
953 status = FwdLockConv_Status_SyntaxError;
954 }
955 } else if (!FwdLockConv_IsWhitespace(ch)) {
956 status = FwdLockConv_Status_SyntaxError;
957 }
958 break;
959 case FwdLockConv_ScannerState_WantsByte2:
960 if (FwdLockConv_IsBase64Char(ch)) {
961 pSession->ch |= base64Values[ch] >> 4;
962 status = FwdLockConv_WriteEncryptedChar(pSession, pSession->ch, pOutput);
963 if (status == FwdLockConv_Status_OK) {
964 pSession->ch = base64Values[ch] << 4;
965 pSession->scannerState = FwdLockConv_ScannerState_WantsByte3;
966 }
967 } else if (ch == '\r') {
968 pSession->savedScannerState = pSession->scannerState;
969 pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
970 } else if (!FwdLockConv_IsWhitespace(ch)) {
971 status = FwdLockConv_Status_SyntaxError;
972 }
973 break;
974 case FwdLockConv_ScannerState_WantsByte3:
975 if (FwdLockConv_IsBase64Char(ch)) {
976 pSession->ch |= base64Values[ch] >> 2;
977 status = FwdLockConv_WriteEncryptedChar(pSession, pSession->ch, pOutput);
978 if (status == FwdLockConv_Status_OK) {
979 pSession->ch = base64Values[ch] << 6;
980 pSession->scannerState = FwdLockConv_ScannerState_WantsByte4;
981 }
982 } else if (ch == '\r') {
983 pSession->savedScannerState = pSession->scannerState;
984 pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
985 } else if (ch == '=') {
986 pSession->scannerState = FwdLockConv_ScannerState_WantsPadding;
987 } else if (!FwdLockConv_IsWhitespace(ch)) {
988 status = FwdLockConv_Status_SyntaxError;
989 }
990 break;
991 case FwdLockConv_ScannerState_WantsByte4:
992 if (FwdLockConv_IsBase64Char(ch)) {
993 pSession->ch |= base64Values[ch];
994 status = FwdLockConv_WriteEncryptedChar(pSession, pSession->ch, pOutput);
995 if (status == FwdLockConv_Status_OK) {
996 pSession->scannerState = FwdLockConv_ScannerState_WantsByte1;
997 }
998 } else if (ch == '\r') {
999 pSession->savedScannerState = pSession->scannerState;
1000 pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
1001 } else if (ch == '=') {
1002 pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace;
1003 } else if (!FwdLockConv_IsWhitespace(ch)) {
1004 status = FwdLockConv_Status_SyntaxError;
1005 }
1006 break;
1007 case FwdLockConv_ScannerState_WantsLF:
1008 if (ch == '\n') {
1009 pSession->scannerState = pSession->savedScannerState;
1010 } else {
1011 status = FwdLockConv_Status_SyntaxError;
1012 }
1013 break;
1014 case FwdLockConv_ScannerState_WantsPadding:
1015 if (ch == '=') {
1016 pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace;
1017 } else {
1018 status = FwdLockConv_Status_SyntaxError;
1019 }
1020 break;
1021 case FwdLockConv_ScannerState_WantsWhitespace:
1022 case FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF:
1023 if (ch == '\r') {
1024 pSession->savedScannerState = FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF;
1025 pSession->scannerState = FwdLockConv_ScannerState_WantsLF;
1026 } else if (ch == '-') {
1027 if (pSession->scannerState == FwdLockConv_ScannerState_WantsWhitespace_AfterCRLF) {
1028 pSession->delimiterMatchPos = 3;
1029 pSession->scannerState = FwdLockConv_ScannerState_WantsDelimiter;
1030 } else {
1031 status = FwdLockConv_Status_SyntaxError;
1032 }
1033 } else if (FwdLockConv_IsWhitespace(ch)) {
1034 pSession->scannerState = FwdLockConv_ScannerState_WantsWhitespace;
1035 } else {
1036 status = FwdLockConv_Status_SyntaxError;
1037 }
1038 break;
1039 case FwdLockConv_ScannerState_WantsDelimiter:
1040 if (ch != pSession->delimiter[pSession->delimiterMatchPos]) {
1041 status = FwdLockConv_Status_SyntaxError;
1042 } else if (++pSession->delimiterMatchPos == pSession->delimiterLength) {
1043 pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash;
1044 }
1045 break;
1046 case FwdLockConv_ScannerState_WantsFirstDash:
1047 if (ch == '-') {
1048 pSession->scannerState = FwdLockConv_ScannerState_WantsSecondDash;
1049 } else {
1050 status = FwdLockConv_Status_SyntaxError;
1051 }
1052 break;
1053 case FwdLockConv_ScannerState_WantsSecondDash:
1054 if (ch == '-') {
1055 pSession->parserState = FwdLockConv_ParserState_Done;
1056 } else {
1057 status = FwdLockConv_Status_SyntaxError;
1058 }
1059 break;
1060 default:
1061 status = FwdLockConv_Status_ProgramError;
1062 break;
1063 }
1064 return status;
1065 }
1066
1067 /**
1068 * Pushes a single character into the converter's state machine.
1069 *
1070 * @param[in,out] pSession A reference to a converter session.
1071 * @param[in] ch A character.
1072 * @param[in,out] pOutput The output from the conversion process.
1073 *
1074 * @return A status code.
1075 */
FwdLockConv_PushChar(FwdLockConv_Session_t * pSession,int ch,FwdLockConv_Output_t * pOutput)1076 static FwdLockConv_Status_t FwdLockConv_PushChar(FwdLockConv_Session_t *pSession,
1077 int ch,
1078 FwdLockConv_Output_t *pOutput) {
1079 FwdLockConv_Status_t status;
1080 ++pSession->numCharsConsumed;
1081 switch (pSession->parserState) {
1082 case FwdLockConv_ParserState_WantsOpenDelimiter:
1083 status = FwdLockConv_MatchOpenDelimiter(pSession, ch);
1084 break;
1085 case FwdLockConv_ParserState_WantsMimeHeaders:
1086 status = FwdLockConv_MatchMimeHeaders(pSession, ch, pOutput);
1087 break;
1088 case FwdLockConv_ParserState_WantsBinaryEncodedData:
1089 status = FwdLockConv_MatchBinaryEncodedData(pSession, ch, pOutput);
1090 break;
1091 case FwdLockConv_ParserState_WantsBase64EncodedData:
1092 if (ch == '\n' && pSession->scannerState != FwdLockConv_ScannerState_WantsLF) {
1093 // Repair base64-encoded data that doesn't have carriage returns in its line breaks.
1094 status = FwdLockConv_MatchBase64EncodedData(pSession, '\r', pOutput);
1095 if (status != FwdLockConv_Status_OK) {
1096 break;
1097 }
1098 }
1099 status = FwdLockConv_MatchBase64EncodedData(pSession, ch, pOutput);
1100 break;
1101 case FwdLockConv_ParserState_Done:
1102 status = FwdLockConv_Status_OK;
1103 break;
1104 default:
1105 status = FwdLockConv_Status_ProgramError;
1106 break;
1107 }
1108 return status;
1109 }
1110
FwdLockConv_OpenSession(int * pSessionId,FwdLockConv_Output_t * pOutput)1111 FwdLockConv_Status_t FwdLockConv_OpenSession(int *pSessionId, FwdLockConv_Output_t *pOutput) {
1112 FwdLockConv_Status_t status;
1113 if (pSessionId == NULL || pOutput == NULL) {
1114 status = FwdLockConv_Status_InvalidArgument;
1115 } else {
1116 *pSessionId = FwdLockConv_AcquireSession();
1117 if (*pSessionId < 0) {
1118 status = FwdLockConv_Status_TooManySessions;
1119 } else {
1120 FwdLockConv_Session_t *pSession = sessionPtrs[*pSessionId];
1121 pSession->encryptedSessionKeyLength = FwdLockGlue_GetEncryptedKeyLength(KEY_SIZE);
1122 if (pSession->encryptedSessionKeyLength < AES_BLOCK_SIZE) {
1123 // The encrypted session key is used as the CTR-mode nonce, so it must be at least
1124 // the size of a single AES block.
1125 status = FwdLockConv_Status_ProgramError;
1126 } else {
1127 pSession->pEncryptedSessionKey = malloc(pSession->encryptedSessionKeyLength);
1128 if (pSession->pEncryptedSessionKey == NULL) {
1129 status = FwdLockConv_Status_OutOfMemory;
1130 } else {
1131 if (!FwdLockGlue_GetRandomNumber(pSession->sessionKey, KEY_SIZE)) {
1132 status = FwdLockConv_Status_RandomNumberGenerationFailed;
1133 } else if (!FwdLockGlue_EncryptKey(pSession->sessionKey, KEY_SIZE,
1134 pSession->pEncryptedSessionKey,
1135 pSession->encryptedSessionKeyLength)) {
1136 status = FwdLockConv_Status_KeyEncryptionFailed;
1137 } else {
1138 status = FwdLockConv_DeriveKeys(pSession);
1139 }
1140 if (status == FwdLockConv_Status_OK) {
1141 memset(pSession->sessionKey, 0, KEY_SIZE); // Zero out key data.
1142 memcpy(pSession->counter, pSession->pEncryptedSessionKey, AES_BLOCK_SIZE);
1143 pSession->parserState = FwdLockConv_ParserState_WantsOpenDelimiter;
1144 pSession->scannerState = FwdLockConv_ScannerState_WantsFirstDash;
1145 pSession->numCharsConsumed = 0;
1146 pSession->delimiterMatchPos = 0;
1147 pSession->mimeHeaderName = nullString;
1148 pSession->contentType = nullString;
1149 pSession->contentTransferEncoding =
1150 FwdLockConv_ContentTransferEncoding_Undefined;
1151 pSession->keyStreamIndex = -1;
1152 pOutput->fromConvertData.pBuffer = NULL;
1153 pOutput->fromConvertData.errorPos = INVALID_OFFSET;
1154 } else {
1155 free(pSession->pEncryptedSessionKey);
1156 }
1157 }
1158 }
1159 if (status != FwdLockConv_Status_OK) {
1160 FwdLockConv_ReleaseSession(*pSessionId);
1161 *pSessionId = -1;
1162 }
1163 }
1164 }
1165 return status;
1166 }
1167
FwdLockConv_ConvertData(int sessionId,const void * pBuffer,size_t numBytes,FwdLockConv_Output_t * pOutput)1168 FwdLockConv_Status_t FwdLockConv_ConvertData(int sessionId,
1169 const void *pBuffer,
1170 size_t numBytes,
1171 FwdLockConv_Output_t *pOutput) {
1172 FwdLockConv_Status_t status;
1173 if (!FwdLockConv_IsValidSession(sessionId) || pBuffer == NULL || pOutput == NULL) {
1174 status = FwdLockConv_Status_InvalidArgument;
1175 } else {
1176 size_t i;
1177 FwdLockConv_Session_t *pSession = sessionPtrs[sessionId];
1178 pSession->dataOffset = 0;
1179 pSession->numDataBytes = 0;
1180 pOutput->fromConvertData.numBytes = 0;
1181 status = FwdLockConv_Status_OK;
1182
1183 for (i = 0; i < numBytes; ++i) {
1184 status = FwdLockConv_PushChar(pSession, ((char *)pBuffer)[i], pOutput);
1185 if (status != FwdLockConv_Status_OK) {
1186 break;
1187 }
1188 }
1189 if (status == FwdLockConv_Status_OK) {
1190 // Update the data signature.
1191 HMAC_Update(&pSession->signingContext,
1192 &((unsigned char *)pOutput->fromConvertData.pBuffer)[pSession->dataOffset],
1193 pSession->numDataBytes);
1194 } else if (status == FwdLockConv_Status_SyntaxError) {
1195 pOutput->fromConvertData.errorPos = pSession->numCharsConsumed;
1196 }
1197 }
1198 return status;
1199 }
1200
FwdLockConv_CloseSession(int sessionId,FwdLockConv_Output_t * pOutput)1201 FwdLockConv_Status_t FwdLockConv_CloseSession(int sessionId, FwdLockConv_Output_t *pOutput) {
1202 FwdLockConv_Status_t status;
1203 if (!FwdLockConv_IsValidSession(sessionId) || pOutput == NULL) {
1204 status = FwdLockConv_Status_InvalidArgument;
1205 } else {
1206 FwdLockConv_Session_t *pSession = sessionPtrs[sessionId];
1207 free(pOutput->fromConvertData.pBuffer);
1208 if (pSession->parserState != FwdLockConv_ParserState_Done) {
1209 pOutput->fromCloseSession.errorPos = pSession->numCharsConsumed;
1210 status = FwdLockConv_Status_SyntaxError;
1211 } else {
1212 // Finalize the data signature.
1213 unsigned int signatureSize = SHA1_HASH_SIZE;
1214 HMAC_Final(&pSession->signingContext, pOutput->fromCloseSession.signatures,
1215 &signatureSize);
1216 if (signatureSize != SHA1_HASH_SIZE) {
1217 status = FwdLockConv_Status_ProgramError;
1218 } else {
1219 // Calculate the header signature, which is a signature of the rest of the header
1220 // including the data signature.
1221 HMAC_Init_ex(&pSession->signingContext, NULL, KEY_SIZE, NULL, NULL);
1222 HMAC_Update(&pSession->signingContext, pSession->topHeader, TOP_HEADER_SIZE);
1223 HMAC_Update(&pSession->signingContext, (unsigned char *)pSession->contentType.ptr,
1224 pSession->contentType.length);
1225 HMAC_Update(&pSession->signingContext, pSession->pEncryptedSessionKey,
1226 pSession->encryptedSessionKeyLength);
1227 HMAC_Update(&pSession->signingContext, pOutput->fromCloseSession.signatures,
1228 SHA1_HASH_SIZE);
1229 HMAC_Final(&pSession->signingContext,
1230 &pOutput->fromCloseSession.signatures[SHA1_HASH_SIZE], &signatureSize);
1231 if (signatureSize != SHA1_HASH_SIZE) {
1232 status = FwdLockConv_Status_ProgramError;
1233 } else {
1234 pOutput->fromCloseSession.fileOffset = TOP_HEADER_SIZE +
1235 pSession->contentType.length + pSession->encryptedSessionKeyLength;
1236 status = FwdLockConv_Status_OK;
1237 }
1238 }
1239 pOutput->fromCloseSession.errorPos = INVALID_OFFSET;
1240 }
1241 free(pSession->mimeHeaderName.ptr);
1242 free(pSession->contentType.ptr);
1243 free(pSession->pEncryptedSessionKey);
1244 HMAC_CTX_cleanup(&pSession->signingContext);
1245 FwdLockConv_ReleaseSession(sessionId);
1246 }
1247 return status;
1248 }
1249
FwdLockConv_ConvertOpenFile(int inputFileDesc,FwdLockConv_ReadFunc_t * fpReadFunc,int outputFileDesc,FwdLockConv_WriteFunc_t * fpWriteFunc,FwdLockConv_LSeekFunc_t * fpLSeekFunc,off64_t * pErrorPos)1250 FwdLockConv_Status_t FwdLockConv_ConvertOpenFile(int inputFileDesc,
1251 FwdLockConv_ReadFunc_t *fpReadFunc,
1252 int outputFileDesc,
1253 FwdLockConv_WriteFunc_t *fpWriteFunc,
1254 FwdLockConv_LSeekFunc_t *fpLSeekFunc,
1255 off64_t *pErrorPos) {
1256 FwdLockConv_Status_t status;
1257 if (pErrorPos != NULL) {
1258 *pErrorPos = INVALID_OFFSET;
1259 }
1260 if (fpReadFunc == NULL || fpWriteFunc == NULL || fpLSeekFunc == NULL || inputFileDesc < 0 ||
1261 outputFileDesc < 0) {
1262 status = FwdLockConv_Status_InvalidArgument;
1263 } else {
1264 char *pReadBuffer = malloc(READ_BUFFER_SIZE);
1265 if (pReadBuffer == NULL) {
1266 status = FwdLockConv_Status_OutOfMemory;
1267 } else {
1268 int sessionId;
1269 FwdLockConv_Output_t output;
1270 status = FwdLockConv_OpenSession(&sessionId, &output);
1271 if (status == FwdLockConv_Status_OK) {
1272 ssize_t numBytesRead;
1273 FwdLockConv_Status_t closeStatus;
1274 while ((numBytesRead =
1275 fpReadFunc(inputFileDesc, pReadBuffer, READ_BUFFER_SIZE)) > 0) {
1276 status = FwdLockConv_ConvertData(sessionId, pReadBuffer, (size_t)numBytesRead,
1277 &output);
1278 if (status == FwdLockConv_Status_OK) {
1279 if (output.fromConvertData.pBuffer != NULL &&
1280 output.fromConvertData.numBytes > 0) {
1281 ssize_t numBytesWritten = fpWriteFunc(outputFileDesc,
1282 output.fromConvertData.pBuffer,
1283 output.fromConvertData.numBytes);
1284 if (numBytesWritten != (ssize_t)output.fromConvertData.numBytes) {
1285 status = FwdLockConv_Status_FileWriteError;
1286 break;
1287 }
1288 }
1289 } else {
1290 if (status == FwdLockConv_Status_SyntaxError && pErrorPos != NULL) {
1291 *pErrorPos = output.fromConvertData.errorPos;
1292 }
1293 break;
1294 }
1295 } // end while
1296 if (numBytesRead < 0) {
1297 status = FwdLockConv_Status_FileReadError;
1298 }
1299 closeStatus = FwdLockConv_CloseSession(sessionId, &output);
1300 if (status == FwdLockConv_Status_OK) {
1301 if (closeStatus != FwdLockConv_Status_OK) {
1302 if (closeStatus == FwdLockConv_Status_SyntaxError && pErrorPos != NULL) {
1303 *pErrorPos = output.fromCloseSession.errorPos;
1304 }
1305 status = closeStatus;
1306 } else if (fpLSeekFunc(outputFileDesc, output.fromCloseSession.fileOffset,
1307 SEEK_SET) < 0) {
1308 status = FwdLockConv_Status_FileSeekError;
1309 } else if (fpWriteFunc(outputFileDesc, output.fromCloseSession.signatures,
1310 FWD_LOCK_SIGNATURES_SIZE) != FWD_LOCK_SIGNATURES_SIZE) {
1311 status = FwdLockConv_Status_FileWriteError;
1312 }
1313 }
1314 }
1315 free(pReadBuffer);
1316 }
1317 }
1318 return status;
1319 }
1320