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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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 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