TI's CC3100 websocket camera demo with Arducam mini ov5642 and freertos. Should work with other M3's. Work in progress test demo.

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers HttpAuth.cpp Source File

HttpAuth.cpp

00001 //*****************************************************************************
00002 // Copyright (C) 2014 Texas Instruments Incorporated
00003 //
00004 // All rights reserved. Property of Texas Instruments Incorporated.
00005 // Restricted rights to use, duplicate or disclose this code are
00006 // granted through contract.
00007 // The program may not be used without the written permission of
00008 // Texas Instruments Incorporated or against the terms and conditions
00009 // stipulated in the agreement under which this program has been supplied,
00010 // and under no circumstances can it be used with non-TI connectivity device.
00011 //
00012 //*****************************************************************************
00013 
00014 #include "HttpAuth.h"
00015 
00016 #ifdef HTTP_CORE_ENABLE_AUTH
00017 
00018 #include <string.h>
00019 #include "HttpString.h"
00020 /**
00021  * @addtogroup HttpAuth
00022  * @{
00023  */
00024 
00025 #define DIGEST_AUTHENTICATION_BUFFER_SIZE (32)
00026 
00027 /**
00028  * This structure holds the HTTP digest-access authentication state
00029  */
00030 struct HttpAuthState
00031 {
00032     /// Last-generated nonce
00033     UINT8 nonce[DIGEST_AUTHENTICATION_BUFFER_SIZE];
00034     /// Last-generated opaque
00035     UINT8 opaque[DIGEST_AUTHENTICATION_BUFFER_SIZE];
00036     /// The hash of the username, realm, and password
00037     UINT8 ha1[DIGEST_AUTHENTICATION_BUFFER_SIZE];
00038 };
00039 
00040 
00041 
00042 /// The global state for digest-access authentication
00043 
00044 static struct HttpAuthState g_authState; // = { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
00045 //                                            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
00046 //                                            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
00047 
00048 ///
00049 MD5_CTX MD5state;
00050 
00051 char    HTTP_AUTH_REALM[]                     =  "cc3200 web server";
00052 
00053 /// Header strings to be used for response headers
00054 char    HTTP_AUTHENTICATE_RESPONSE_REALM[]    =  "WWW-Authenticate: Digest realm=\"";
00055 char    HTTP_AUTHENTICATE_RESPONSE_NONCE[]    =  "\",qop=\"auth\",nonce=\"";
00056 char    HTTP_AUTHENTICATE_RESPONSE_OPAQUE[]   =  "\",opaque=\"";
00057 char    HTTP_AUTHENTICATE_RESPONSE_EOH[]      =  "\"\r\n";
00058 
00059 /// Authenticate header tokens
00060 char    HTTP_AUTHENTICATE_REALM[]      =  "realm";
00061 char    HTTP_AUTHENTICATE_QOP[]        =  "qop";
00062 char    HTTP_AUTHENTICATE_AUTH[]       =  "auth";
00063 char    HTTP_AUTHENTICATE_NONCE[]      =  "nonce";
00064 char    HTTP_AUTHENTICATE_OPAQUE[]     =  "opaque";
00065 char    HTTP_AUTHENTICATE_DIGEST[]     =  "digest";
00066 char    HTTP_AUTHENTICATE_URI[]        =  "uri";
00067 char    HTTP_AUTHENTICATE_NC[]         =  "nc=";
00068 char    HTTP_AUTHENTICATE_CNONCE[]     =  "cnonce";
00069 char    HTTP_AUTHENTICATE_RESPONSE[]   =  "response";
00070 char    HTTP_AUTHENTICATE_USERNAME[]   =  "username";
00071 char    HTTP_DELIMITER_QUOTE[]         =  "\"";
00072 char    HTTP_DELIMITER_COMMA[]         =  ",";
00073 
00074 
00075 /// The length of the response authentication header
00076 /// The two 32 numbers represent nonce and opaque strings and the -5 is to compensate for the sizeof() calls
00077 UINT16  HTTP_AUTHENTICATE_RESPONSE_HEADER_LENGTH =
00078                 sizeof(HTTP_AUTHENTICATE_RESPONSE_REALM) +
00079                 sizeof(HTTP_AUTHENTICATE_RESPONSE_NONCE) +
00080                 sizeof(HTTP_AUTHENTICATE_RESPONSE_OPAQUE) +
00081                 sizeof(HTTP_AUTHENTICATE_RESPONSE_EOH) +
00082                 sizeof(HTTP_AUTH_REALM) +
00083                 DIGEST_AUTHENTICATION_BUFFER_SIZE + DIGEST_AUTHENTICATION_BUFFER_SIZE - 5;
00084 
00085 
00086 /**
00087  * Simple random generator
00088  * To improve randomness the initial seed has to be dynamic
00089  */
00090 static UINT32 GetRandomUint()
00091 {
00092     // set static seed
00093     static UINT32 m_z = 1234;
00094     static UINT32 m_w = 98877;
00095 
00096     m_z = 36969 * (m_z & 65535) + (m_z >> 16);
00097     m_w = 18000 * (m_w & 65535) + (m_w >> 16);
00098     return (m_z << 16) + m_w;
00099 }
00100 
00101 static void MD5_FinalToString(UINT8* str, MD5_CTX *md5stat)
00102 {
00103     UINT8 tmp[16];
00104     UINT8 i;
00105     struct HttpBlob location;
00106     location.uLength = 2;
00107     location.pData = str;
00108     MD5_Final(tmp, md5stat);
00109     for (i=0; i<16; i++, location.pData += 2)
00110         HttpString_htoa(tmp[i], &location, 1);
00111 }
00112 
00113 
00114 /**
00115  * This function will generate random 16 bytes to be used for Nonce and opaque strings
00116  */
00117 static void Generate32BytesRandomString(UINT8 *str)
00118 {
00119     struct HttpBlob str1;
00120     str1.uLength = 8;
00121     str1.pData = str;
00122     HttpString_htoa(GetRandomUint(), &str1, 1);
00123     str1.pData = str + 8;
00124     HttpString_htoa(GetRandomUint(), &str1, 1);
00125     str1.pData = str + 16;
00126     HttpString_htoa(GetRandomUint(), &str1, 1);
00127     str1.pData = str + 24;
00128     HttpString_htoa(GetRandomUint(), &str1, 1);
00129 }
00130 
00131 void HttpAuth_Init(struct HttpBlob username, struct HttpBlob password)
00132 {
00133     MD5_Init(&MD5state);
00134     MD5_Update(&MD5state, username.pData, username.uLength);
00135     MD5_Update(&MD5state, ":", 1);
00136     MD5_Update(&MD5state, HTTP_AUTH_REALM, sizeof(HTTP_AUTH_REALM)-1);
00137     MD5_Update(&MD5state, ":", 1);
00138     MD5_Update(&MD5state, password.pData, password.uLength);
00139     MD5_FinalToString(g_authState.ha1, &MD5state);
00140 }
00141 
00142 static void AddStringToBlob(struct HttpBlob * trgt, char *str, UINT16 length)
00143 {
00144     memcpy(trgt->pData + trgt->uLength, str, length);
00145     trgt->uLength += length;
00146 }
00147 
00148 void HttpAuth_ResponseAuthenticate(struct HttpRequest* pRequest, struct HttpBlob* pWWWAuthenticate)
00149 {
00150     struct HttpBlob headerBlob;
00151 
00152     if (pWWWAuthenticate->uLength < HTTP_AUTHENTICATE_RESPONSE_HEADER_LENGTH)
00153     {
00154         pWWWAuthenticate->uLength = 0;
00155         return;
00156     }
00157     // There is enough space to add the authenticate header
00158 
00159     headerBlob.pData = pWWWAuthenticate->pData;
00160     headerBlob.uLength = 0;
00161 
00162     // Generate new Nonce and opaque
00163     Generate32BytesRandomString(g_authState.nonce);
00164     Generate32BytesRandomString(g_authState.opaque);
00165 
00166     // Build response header
00167     AddStringToBlob(&headerBlob, HTTP_AUTHENTICATE_RESPONSE_REALM, sizeof(HTTP_AUTHENTICATE_RESPONSE_REALM)-1);
00168     AddStringToBlob(&headerBlob, HTTP_AUTH_REALM, sizeof(HTTP_AUTH_REALM) -1);
00169     AddStringToBlob(&headerBlob, HTTP_AUTHENTICATE_RESPONSE_NONCE, sizeof(HTTP_AUTHENTICATE_RESPONSE_NONCE) -1);
00170     AddStringToBlob(&headerBlob, (char*)g_authState.nonce, sizeof(g_authState.nonce));
00171     AddStringToBlob(&headerBlob, HTTP_AUTHENTICATE_RESPONSE_OPAQUE, sizeof(HTTP_AUTHENTICATE_RESPONSE_OPAQUE) -1);
00172     AddStringToBlob(&headerBlob, (char*)g_authState.opaque, sizeof(g_authState.opaque));
00173     AddStringToBlob(&headerBlob, HTTP_AUTHENTICATE_RESPONSE_EOH, sizeof(HTTP_AUTHENTICATE_RESPONSE_EOH) - 1);
00174 
00175     pWWWAuthenticate->uLength = headerBlob.uLength;
00176 }
00177 
00178 /**
00179  * Find/verify name value pair in the input blob
00180  *
00181  * After return the location is stays in the same place since the order of the name value pairs in not constant
00182  * Returns:     if header not found return 0
00183  *              if value token is NULL - return pointer to start of value length of value till the \" delimiter
00184  *              if value token is not NULL - return 1 if values match
00185  */
00186 static UINT16 HttpAuth_VerifyHeaderNameValue(struct HttpBlob *location, char* nameToken, UINT8 tokenlenLen, char *value, UINT8 valuelen, char** outValue)
00187 {
00188     UINT8 * found;
00189     struct HttpBlob originalLocation;
00190     originalLocation.uLength = location->uLength;
00191     originalLocation.pData = location->pData;
00192 
00193     found = HttpString_nextToken(nameToken, tokenlenLen, *location);
00194 
00195     // Missing header name
00196     if (found == 0)
00197         return 0;
00198     else
00199     {
00200         location->uLength = originalLocation.uLength - (UINT16)(found - originalLocation.pData) - (tokenlenLen + 2);
00201         location->pData = found + tokenlenLen + 2;
00202         // Return the value pointer and size
00203         if (value == NULL)
00204         {
00205             *outValue= (char*)location->pData;
00206             nameToken = (char *)location->pData;
00207             found = HttpString_nextToken(HTTP_DELIMITER_QUOTE, sizeof(HTTP_DELIMITER_QUOTE) - 1, *location);
00208             if (found==0)
00209                 found = HttpString_nextToken(HTTP_DELIMITER_COMMA, sizeof(HTTP_DELIMITER_COMMA) - 1, *location);
00210 
00211             // Restore current location
00212             location->uLength = originalLocation.uLength;
00213             location->pData = originalLocation.pData;
00214             return (UINT16)((char *)found - nameToken);
00215         }
00216 
00217         found = HttpString_nextToken(value, valuelen, *location);
00218         // Value does not match - restore location
00219         if (found == 0)
00220         {
00221             location->uLength = originalLocation.uLength;
00222             location->pData = originalLocation.pData;
00223             return 0;
00224         }
00225 
00226         // Restore location
00227         location->uLength = originalLocation.uLength;
00228         location->pData = originalLocation.pData;
00229         return 1;
00230     }
00231 }
00232 
00233 
00234 void HttpAuth_RequestAuthenticate(struct HttpRequest* pRequest, struct HttpBlob authorization)
00235 {
00236     UINT8 ha2[DIGEST_AUTHENTICATION_BUFFER_SIZE], correctResponse[DIGEST_AUTHENTICATION_BUFFER_SIZE];
00237     struct HttpBlob currentLocation, blob;
00238     // HA1 was not copmuted
00239     if ((UINT32)*g_authState.ha1 == 0)
00240     {
00241         return;
00242     }
00243 
00244     // Parse the header - find relevant tokens and handle
00245     currentLocation.pData = authorization.pData;
00246     currentLocation.uLength = authorization.uLength;
00247 
00248     // Verify the mandatory tokens, whose content we ignore are present
00249 
00250     // verify we are in degest authentication method, any other is not supported
00251     if (HttpString_nextToken(HTTP_AUTHENTICATE_DIGEST, sizeof(HTTP_AUTHENTICATE_DIGEST)-1, currentLocation) == 0)
00252         return;
00253 
00254     // verify username exists
00255     if (HttpString_nextToken(HTTP_AUTHENTICATE_USERNAME, sizeof(HTTP_AUTHENTICATE_USERNAME)-1, currentLocation) == 0)
00256         return;
00257 
00258     // Verify realm
00259     if (HttpAuth_VerifyHeaderNameValue(&currentLocation, HTTP_AUTHENTICATE_REALM, sizeof(HTTP_AUTHENTICATE_REALM)-1, HTTP_AUTH_REALM, sizeof(HTTP_AUTH_REALM)-1, 0) != 1)
00260         return;
00261 
00262     // Verify correct nonce
00263     if (HttpAuth_VerifyHeaderNameValue(&currentLocation, HTTP_AUTHENTICATE_NONCE, sizeof(HTTP_AUTHENTICATE_NONCE)-1, (char *)g_authState.nonce, DIGEST_AUTHENTICATION_BUFFER_SIZE, 0) != 1)
00264         return;
00265 
00266     // Verify correct opaque
00267     if (HttpAuth_VerifyHeaderNameValue(&currentLocation, HTTP_AUTHENTICATE_OPAQUE, sizeof(HTTP_AUTHENTICATE_OPAQUE)-1, (char *)g_authState.opaque, DIGEST_AUTHENTICATION_BUFFER_SIZE, 0) != 1)
00268         return;
00269 
00270     // Find neccessary tokents and compute HA2 if some tokens are not found - return
00271     blob.pData = NULL;
00272     blob.uLength = HttpAuth_VerifyHeaderNameValue(&currentLocation, HTTP_AUTHENTICATE_URI, sizeof(HTTP_AUTHENTICATE_URI)-1, 0, 0, (char**)&blob.pData);
00273     // Uri is missing
00274     if (blob.uLength == 0)
00275         return;
00276 
00277     MD5_Init(&MD5state);
00278     if ((pRequest->uFlags & HTTP_REQUEST_FLAG_METHOD_POST) != 0)
00279         MD5_Update(&MD5state, "POST", 4);
00280     else
00281         MD5_Update(&MD5state, "GET", 3);
00282     MD5_Update(&MD5state, ":", 1);
00283     MD5_Update(&MD5state, blob.pData, blob.uLength);
00284     MD5_FinalToString(ha2, &MD5state);
00285 
00286     // Find tokens to compute correct response
00287     blob.pData = HttpString_nextToken(HTTP_AUTHENTICATE_NC, sizeof(HTTP_AUTHENTICATE_NC)-1, currentLocation);
00288     // Ncount is missing
00289     if (blob.pData == 0)
00290         return;
00291     blob.pData += sizeof(HTTP_AUTHENTICATE_NC) - 1;
00292     blob.uLength = 8;
00293 
00294     MD5_Init(&MD5state);
00295     MD5_Update(&MD5state, g_authState.ha1, DIGEST_AUTHENTICATION_BUFFER_SIZE);
00296     MD5_Update(&MD5state, ":", 1);
00297     MD5_Update(&MD5state, g_authState.nonce, DIGEST_AUTHENTICATION_BUFFER_SIZE);
00298     MD5_Update(&MD5state, ":", 1);
00299     MD5_Update(&MD5state, blob.pData, blob.uLength);
00300 
00301     blob.pData = NULL;
00302     blob.uLength = HttpAuth_VerifyHeaderNameValue(&currentLocation, HTTP_AUTHENTICATE_CNONCE, sizeof(HTTP_AUTHENTICATE_CNONCE)-1, 0, 0, (char **)&blob.pData);
00303     // Cnonce is missing
00304     if (blob.uLength == 0)
00305         return;
00306     MD5_Update(&MD5state, ":", 1);
00307     MD5_Update(&MD5state, blob.pData, blob.uLength);
00308     MD5_Update(&MD5state, ":auth:", 6);
00309     MD5_Update(&MD5state, ha2, DIGEST_AUTHENTICATION_BUFFER_SIZE);
00310     MD5_FinalToString(correctResponse, &MD5state);
00311 
00312     // Compare received response to the one computed locally - if equal then authorize
00313     blob.pData = NULL;
00314     blob.uLength = HttpAuth_VerifyHeaderNameValue(&currentLocation, HTTP_AUTHENTICATE_RESPONSE, sizeof(HTTP_AUTHENTICATE_RESPONSE)-1, 0, 0, (char **)&blob.pData);
00315     // Response is missing
00316     if (blob.uLength != DIGEST_AUTHENTICATION_BUFFER_SIZE)
00317         return;
00318 
00319     currentLocation.pData = correctResponse;
00320     currentLocation.uLength = DIGEST_AUTHENTICATION_BUFFER_SIZE;
00321     // if the responses are equal
00322     if (HttpString_strcmp(blob, currentLocation) == 0)
00323         pRequest->uFlags |= HTTP_REQUEST_FLAG_AUTHENTICATED;
00324 
00325 }
00326 #endif
00327 
00328 /// @}
00329 
00330