TI's CC3100 websocket camera demo with Arducam mini ov5642 and freertos. Should work with other M3's. Work in progress test demo.
Diff: http/server/HttpAuth.cpp
- Revision:
- 0:400d8e75a8d0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/http/server/HttpAuth.cpp Sun Sep 06 15:19:36 2015 +0000 @@ -0,0 +1,330 @@ +//***************************************************************************** +// Copyright (C) 2014 Texas Instruments Incorporated +// +// All rights reserved. Property of Texas Instruments Incorporated. +// Restricted rights to use, duplicate or disclose this code are +// granted through contract. +// The program may not be used without the written permission of +// Texas Instruments Incorporated or against the terms and conditions +// stipulated in the agreement under which this program has been supplied, +// and under no circumstances can it be used with non-TI connectivity device. +// +//***************************************************************************** + +#include "HttpAuth.h" + +#ifdef HTTP_CORE_ENABLE_AUTH + +#include <string.h> +#include "HttpString.h" +/** + * @addtogroup HttpAuth + * @{ + */ + +#define DIGEST_AUTHENTICATION_BUFFER_SIZE (32) + +/** + * This structure holds the HTTP digest-access authentication state + */ +struct HttpAuthState +{ + /// Last-generated nonce + UINT8 nonce[DIGEST_AUTHENTICATION_BUFFER_SIZE]; + /// Last-generated opaque + UINT8 opaque[DIGEST_AUTHENTICATION_BUFFER_SIZE]; + /// The hash of the username, realm, and password + UINT8 ha1[DIGEST_AUTHENTICATION_BUFFER_SIZE]; +}; + + + +/// The global state for digest-access authentication + +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}, +// {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}, +// {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}}; + +/// +MD5_CTX MD5state; + +char HTTP_AUTH_REALM[] = "cc3200 web server"; + +/// Header strings to be used for response headers +char HTTP_AUTHENTICATE_RESPONSE_REALM[] = "WWW-Authenticate: Digest realm=\""; +char HTTP_AUTHENTICATE_RESPONSE_NONCE[] = "\",qop=\"auth\",nonce=\""; +char HTTP_AUTHENTICATE_RESPONSE_OPAQUE[] = "\",opaque=\""; +char HTTP_AUTHENTICATE_RESPONSE_EOH[] = "\"\r\n"; + +/// Authenticate header tokens +char HTTP_AUTHENTICATE_REALM[] = "realm"; +char HTTP_AUTHENTICATE_QOP[] = "qop"; +char HTTP_AUTHENTICATE_AUTH[] = "auth"; +char HTTP_AUTHENTICATE_NONCE[] = "nonce"; +char HTTP_AUTHENTICATE_OPAQUE[] = "opaque"; +char HTTP_AUTHENTICATE_DIGEST[] = "digest"; +char HTTP_AUTHENTICATE_URI[] = "uri"; +char HTTP_AUTHENTICATE_NC[] = "nc="; +char HTTP_AUTHENTICATE_CNONCE[] = "cnonce"; +char HTTP_AUTHENTICATE_RESPONSE[] = "response"; +char HTTP_AUTHENTICATE_USERNAME[] = "username"; +char HTTP_DELIMITER_QUOTE[] = "\""; +char HTTP_DELIMITER_COMMA[] = ","; + + +/// The length of the response authentication header +/// The two 32 numbers represent nonce and opaque strings and the -5 is to compensate for the sizeof() calls +UINT16 HTTP_AUTHENTICATE_RESPONSE_HEADER_LENGTH = + sizeof(HTTP_AUTHENTICATE_RESPONSE_REALM) + + sizeof(HTTP_AUTHENTICATE_RESPONSE_NONCE) + + sizeof(HTTP_AUTHENTICATE_RESPONSE_OPAQUE) + + sizeof(HTTP_AUTHENTICATE_RESPONSE_EOH) + + sizeof(HTTP_AUTH_REALM) + + DIGEST_AUTHENTICATION_BUFFER_SIZE + DIGEST_AUTHENTICATION_BUFFER_SIZE - 5; + + +/** + * Simple random generator + * To improve randomness the initial seed has to be dynamic + */ +static UINT32 GetRandomUint() +{ + // set static seed + static UINT32 m_z = 1234; + static UINT32 m_w = 98877; + + m_z = 36969 * (m_z & 65535) + (m_z >> 16); + m_w = 18000 * (m_w & 65535) + (m_w >> 16); + return (m_z << 16) + m_w; +} + +static void MD5_FinalToString(UINT8* str, MD5_CTX *md5stat) +{ + UINT8 tmp[16]; + UINT8 i; + struct HttpBlob location; + location.uLength = 2; + location.pData = str; + MD5_Final(tmp, md5stat); + for (i=0; i<16; i++, location.pData += 2) + HttpString_htoa(tmp[i], &location, 1); +} + + +/** + * This function will generate random 16 bytes to be used for Nonce and opaque strings + */ +static void Generate32BytesRandomString(UINT8 *str) +{ + struct HttpBlob str1; + str1.uLength = 8; + str1.pData = str; + HttpString_htoa(GetRandomUint(), &str1, 1); + str1.pData = str + 8; + HttpString_htoa(GetRandomUint(), &str1, 1); + str1.pData = str + 16; + HttpString_htoa(GetRandomUint(), &str1, 1); + str1.pData = str + 24; + HttpString_htoa(GetRandomUint(), &str1, 1); +} + +void HttpAuth_Init(struct HttpBlob username, struct HttpBlob password) +{ + MD5_Init(&MD5state); + MD5_Update(&MD5state, username.pData, username.uLength); + MD5_Update(&MD5state, ":", 1); + MD5_Update(&MD5state, HTTP_AUTH_REALM, sizeof(HTTP_AUTH_REALM)-1); + MD5_Update(&MD5state, ":", 1); + MD5_Update(&MD5state, password.pData, password.uLength); + MD5_FinalToString(g_authState.ha1, &MD5state); +} + +static void AddStringToBlob(struct HttpBlob * trgt, char *str, UINT16 length) +{ + memcpy(trgt->pData + trgt->uLength, str, length); + trgt->uLength += length; +} + +void HttpAuth_ResponseAuthenticate(struct HttpRequest* pRequest, struct HttpBlob* pWWWAuthenticate) +{ + struct HttpBlob headerBlob; + + if (pWWWAuthenticate->uLength < HTTP_AUTHENTICATE_RESPONSE_HEADER_LENGTH) + { + pWWWAuthenticate->uLength = 0; + return; + } + // There is enough space to add the authenticate header + + headerBlob.pData = pWWWAuthenticate->pData; + headerBlob.uLength = 0; + + // Generate new Nonce and opaque + Generate32BytesRandomString(g_authState.nonce); + Generate32BytesRandomString(g_authState.opaque); + + // Build response header + AddStringToBlob(&headerBlob, HTTP_AUTHENTICATE_RESPONSE_REALM, sizeof(HTTP_AUTHENTICATE_RESPONSE_REALM)-1); + AddStringToBlob(&headerBlob, HTTP_AUTH_REALM, sizeof(HTTP_AUTH_REALM) -1); + AddStringToBlob(&headerBlob, HTTP_AUTHENTICATE_RESPONSE_NONCE, sizeof(HTTP_AUTHENTICATE_RESPONSE_NONCE) -1); + AddStringToBlob(&headerBlob, (char*)g_authState.nonce, sizeof(g_authState.nonce)); + AddStringToBlob(&headerBlob, HTTP_AUTHENTICATE_RESPONSE_OPAQUE, sizeof(HTTP_AUTHENTICATE_RESPONSE_OPAQUE) -1); + AddStringToBlob(&headerBlob, (char*)g_authState.opaque, sizeof(g_authState.opaque)); + AddStringToBlob(&headerBlob, HTTP_AUTHENTICATE_RESPONSE_EOH, sizeof(HTTP_AUTHENTICATE_RESPONSE_EOH) - 1); + + pWWWAuthenticate->uLength = headerBlob.uLength; +} + +/** + * Find/verify name value pair in the input blob + * + * After return the location is stays in the same place since the order of the name value pairs in not constant + * Returns: if header not found return 0 + * if value token is NULL - return pointer to start of value length of value till the \" delimiter + * if value token is not NULL - return 1 if values match + */ +static UINT16 HttpAuth_VerifyHeaderNameValue(struct HttpBlob *location, char* nameToken, UINT8 tokenlenLen, char *value, UINT8 valuelen, char** outValue) +{ + UINT8 * found; + struct HttpBlob originalLocation; + originalLocation.uLength = location->uLength; + originalLocation.pData = location->pData; + + found = HttpString_nextToken(nameToken, tokenlenLen, *location); + + // Missing header name + if (found == 0) + return 0; + else + { + location->uLength = originalLocation.uLength - (UINT16)(found - originalLocation.pData) - (tokenlenLen + 2); + location->pData = found + tokenlenLen + 2; + // Return the value pointer and size + if (value == NULL) + { + *outValue= (char*)location->pData; + nameToken = (char *)location->pData; + found = HttpString_nextToken(HTTP_DELIMITER_QUOTE, sizeof(HTTP_DELIMITER_QUOTE) - 1, *location); + if (found==0) + found = HttpString_nextToken(HTTP_DELIMITER_COMMA, sizeof(HTTP_DELIMITER_COMMA) - 1, *location); + + // Restore current location + location->uLength = originalLocation.uLength; + location->pData = originalLocation.pData; + return (UINT16)((char *)found - nameToken); + } + + found = HttpString_nextToken(value, valuelen, *location); + // Value does not match - restore location + if (found == 0) + { + location->uLength = originalLocation.uLength; + location->pData = originalLocation.pData; + return 0; + } + + // Restore location + location->uLength = originalLocation.uLength; + location->pData = originalLocation.pData; + return 1; + } +} + + +void HttpAuth_RequestAuthenticate(struct HttpRequest* pRequest, struct HttpBlob authorization) +{ + UINT8 ha2[DIGEST_AUTHENTICATION_BUFFER_SIZE], correctResponse[DIGEST_AUTHENTICATION_BUFFER_SIZE]; + struct HttpBlob currentLocation, blob; + // HA1 was not copmuted + if ((UINT32)*g_authState.ha1 == 0) + { + return; + } + + // Parse the header - find relevant tokens and handle + currentLocation.pData = authorization.pData; + currentLocation.uLength = authorization.uLength; + + // Verify the mandatory tokens, whose content we ignore are present + + // verify we are in degest authentication method, any other is not supported + if (HttpString_nextToken(HTTP_AUTHENTICATE_DIGEST, sizeof(HTTP_AUTHENTICATE_DIGEST)-1, currentLocation) == 0) + return; + + // verify username exists + if (HttpString_nextToken(HTTP_AUTHENTICATE_USERNAME, sizeof(HTTP_AUTHENTICATE_USERNAME)-1, currentLocation) == 0) + return; + + // Verify realm + if (HttpAuth_VerifyHeaderNameValue(¤tLocation, HTTP_AUTHENTICATE_REALM, sizeof(HTTP_AUTHENTICATE_REALM)-1, HTTP_AUTH_REALM, sizeof(HTTP_AUTH_REALM)-1, 0) != 1) + return; + + // Verify correct nonce + if (HttpAuth_VerifyHeaderNameValue(¤tLocation, HTTP_AUTHENTICATE_NONCE, sizeof(HTTP_AUTHENTICATE_NONCE)-1, (char *)g_authState.nonce, DIGEST_AUTHENTICATION_BUFFER_SIZE, 0) != 1) + return; + + // Verify correct opaque + if (HttpAuth_VerifyHeaderNameValue(¤tLocation, HTTP_AUTHENTICATE_OPAQUE, sizeof(HTTP_AUTHENTICATE_OPAQUE)-1, (char *)g_authState.opaque, DIGEST_AUTHENTICATION_BUFFER_SIZE, 0) != 1) + return; + + // Find neccessary tokents and compute HA2 if some tokens are not found - return + blob.pData = NULL; + blob.uLength = HttpAuth_VerifyHeaderNameValue(¤tLocation, HTTP_AUTHENTICATE_URI, sizeof(HTTP_AUTHENTICATE_URI)-1, 0, 0, (char**)&blob.pData); + // Uri is missing + if (blob.uLength == 0) + return; + + MD5_Init(&MD5state); + if ((pRequest->uFlags & HTTP_REQUEST_FLAG_METHOD_POST) != 0) + MD5_Update(&MD5state, "POST", 4); + else + MD5_Update(&MD5state, "GET", 3); + MD5_Update(&MD5state, ":", 1); + MD5_Update(&MD5state, blob.pData, blob.uLength); + MD5_FinalToString(ha2, &MD5state); + + // Find tokens to compute correct response + blob.pData = HttpString_nextToken(HTTP_AUTHENTICATE_NC, sizeof(HTTP_AUTHENTICATE_NC)-1, currentLocation); + // Ncount is missing + if (blob.pData == 0) + return; + blob.pData += sizeof(HTTP_AUTHENTICATE_NC) - 1; + blob.uLength = 8; + + MD5_Init(&MD5state); + MD5_Update(&MD5state, g_authState.ha1, DIGEST_AUTHENTICATION_BUFFER_SIZE); + MD5_Update(&MD5state, ":", 1); + MD5_Update(&MD5state, g_authState.nonce, DIGEST_AUTHENTICATION_BUFFER_SIZE); + MD5_Update(&MD5state, ":", 1); + MD5_Update(&MD5state, blob.pData, blob.uLength); + + blob.pData = NULL; + blob.uLength = HttpAuth_VerifyHeaderNameValue(¤tLocation, HTTP_AUTHENTICATE_CNONCE, sizeof(HTTP_AUTHENTICATE_CNONCE)-1, 0, 0, (char **)&blob.pData); + // Cnonce is missing + if (blob.uLength == 0) + return; + MD5_Update(&MD5state, ":", 1); + MD5_Update(&MD5state, blob.pData, blob.uLength); + MD5_Update(&MD5state, ":auth:", 6); + MD5_Update(&MD5state, ha2, DIGEST_AUTHENTICATION_BUFFER_SIZE); + MD5_FinalToString(correctResponse, &MD5state); + + // Compare received response to the one computed locally - if equal then authorize + blob.pData = NULL; + blob.uLength = HttpAuth_VerifyHeaderNameValue(¤tLocation, HTTP_AUTHENTICATE_RESPONSE, sizeof(HTTP_AUTHENTICATE_RESPONSE)-1, 0, 0, (char **)&blob.pData); + // Response is missing + if (blob.uLength != DIGEST_AUTHENTICATION_BUFFER_SIZE) + return; + + currentLocation.pData = correctResponse; + currentLocation.uLength = DIGEST_AUTHENTICATION_BUFFER_SIZE; + // if the responses are equal + if (HttpString_strcmp(blob, currentLocation) == 0) + pRequest->uFlags |= HTTP_REQUEST_FLAG_AUTHENTICATED; + +} +#endif + +/// @} + +