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