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