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