Port of TI's CC3100 Websock camera demo. Using FreeRTOS, mbedTLS, also parts of Arducam for cams ov5642 and 0v2640. Can also use MT9D111. Work in progress. Be warned some parts maybe a bit flacky. This is for Seeed Arch max only, for an M3, see the demo for CM3 using the 0v5642 aducam mini.

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