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