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