Click here to Skip to main content
15,897,891 members
Articles / Programming Languages / C++

Small and Reliable C++ HTTP Server with Complete ASP.NET Support

Rate me:
Please Sign up or sign in to vote.
4.78/5 (24 votes)
5 Feb 2009CPOL8 min read 74.5K   1.6K   81  
This article describes results of ahttpserver evolution - implementation of ASP.NET handler and many architecture improvements
/*
This file is part of [aconnect] library. 

Author: Artem Kustikov (kustikoff[at]tut.by)
version: 0.19

This code is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this code.

Permission is granted to anyone to use this code for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:

1. The origin of this code must not be misrepresented; you must
not claim that you wrote the original code. If you use this
code in a product, an acknowledgment in the product documentation
would be appreciated but is not required.

2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original code.

3. This notice may not be removed or altered from any source
distribution.
*/

/*
	Idea got from SHA-1 (Secure Hashing Algorithm) implementation
	by Paul E. Jones.

	----------------------------------------------------------------
	Copyright (C) 1998
	Paul E. Jones <paulej@arid.us>
	All Rights Reserved.

	This software is licensed as "freeware."  Permission to distribute
	this software in source and binary forms is hereby granted without
	a fee.  THIS SOFTWARE IS PROVIDED 'AS IS' AND WITHOUT ANY EXPRESSED
	OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
	WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
	THE AUTHOR SHALL NOT BE HELD LIABLE FOR ANY DAMAGES RESULTING
	FROM THE USE OF THIS SOFTWARE, EITHER DIRECTLY OR INDIRECTLY, INCLUDING,
	BUT NOT LIMITED TO, LOSS OF DATA OR DATA BEING RENDERED INACCURATE.
	----------------------------------------------------------------
*/

#ifndef ACONNECT_CRYPTO_SHA1_H
#define ACONNECT_CRYPTO_SHA1_H

#include <stdexcept>

#include "aconnect/types.hpp"

namespace aconnect { namespace crypto {
	
	typedef unsigned int variable_type; // sizeof(variable_type) >= sizeof(WORD)

	const int Sha1DigestLength = 5; // words
	const char_type HexSymbolsList[] = "0123456789abcdef";
	

	struct sha1
	{
		variable_type	digest[Sha1DigestLength];			// Message Digest (output)
		char_type		digestBytes[Sha1DigestLength * 4];	// Message Digest in bytes 

		variable_type lengthLow;			// Message length in bits (low word)
		variable_type lengthHigh;			// Message length in bits (hi word)

		byte_type messageBlock[64];			// 512-bit message blocks - type for byte must be unsigned
		int messageBlockIndex;				// Index into message block array

		bool computed;						// Is the digest computed?
		bool corrupted;						// Is the message digest corruped?

		inline sha1 ();
		inline sha1 (string_constref input);

		///////////////////////////////////////////////////////////////////////////////
		//
		//		Methods
		
		/**
		*	Initialize the sha1 object in preparation for computing a new message digest.
		*/
		inline void reset ();

		inline void append (string_constref input);
		inline void append (string_constptr input, string::size_type inputSize);

		/**
		*	According to the standard, the message must be padded to an even
		*   512 bits.  The first padding bit must be a '1'.  The last 64
		*   bits represent the length of the original message.  All bits in
		*   between should be 0.  This function will pad the message
		*   according to those rules by filling the 'messageBlock' array
		*   accordingly.  It will also call processMessageBlock()
		*   appropriately.  When it returns, it can be assumed that the
		*   message digest has been computed. 
		*/
		inline void calculate ();

		/**
		*	Process the current 512 bits of the message stored in the messageBlock array
		*/
		inline void processMessageBlock();

		inline bool isValid ();

		/**
		* Return calculated SHA-1 hash
		* @param[in]	formatHex - Flag shows whether format output in hex
		*/
		inline string result (bool formatHex = true) throw (std::runtime_error);

		inline void processDigest (string_constptr input, string::size_type inputSize, char_type *outDigest) throw (std::runtime_error);

		inline void validate()  throw (std::runtime_error);


		/**
		*	Perform following sequence: reset/append/result
		*	@param[in]	input - Data to calculate digest
		*	@param[in]	formatHex - Flag shows whether format output in hex
		*/
		inline string process (string_constref input, bool formatHex = true) throw (std::runtime_error);

		// helpers
		inline static variable_type leftRotate (int bits, variable_type in);
	};

	sha1::sha1() 
	{
		reset();
	}

	sha1::sha1 (string_constref input) {
		reset();
		append (input);
	}

	void sha1::reset () {
		lengthLow = lengthHigh = 0;
		messageBlockIndex = 0;

		digest[0]      = 0x67452301;
		digest[1]      = 0xEFCDAB89;
		digest[2]      = 0x98BADCFE;
		digest[3]      = 0x10325476;
		digest[4]      = 0xC3D2E1F0;

		computed = false;
		corrupted  = false;
	}
	
	bool sha1::isValid () {
		return !corrupted;
	}

	void sha1::append (string_constref input) {
		append (input.c_str(), input.size());
	}
	
	void sha1::append (string_constptr input, string::size_type inputSize) 
	{
		if (!inputSize)
			return;
		
		// calculated hash cannot be modified
		if (computed || corrupted) {
			corrupted = true;
			return;
		}

		while(inputSize-- && !corrupted)
		{
			messageBlock[messageBlockIndex++] = (byte_type) *input;

			lengthLow += 8;
			
			// Force it to 32 bits
			lengthLow &= 0xFFFFFFFF;

			if (lengthLow == 0)
			{
				++lengthHigh;

				// Force it to 32 bits
				lengthHigh &= 0xFFFFFFFF;
				if (lengthHigh == 0)
				{
					// Message is too long
					corrupted = true;
					return;
				}
			}

			if (messageBlockIndex == 64)
				processMessageBlock();
			
			++input;
		}
	}

	void sha1::validate() throw (std::runtime_error) {
		if (corrupted)
			throw std::runtime_error("SHA-1 hash calculation corrupted");
	}

	string sha1::result(bool formatHex)  throw (std::runtime_error) 
	{
		validate ();

		if (!computed)
			calculate ();
		
		if (formatHex) 
		{
			string res ("0000000000000000000000000000000000000000");
			
			for (int ndx=0; ndx<Sha1DigestLength * 4; ++ndx) {
				res[ndx*2] = HexSymbolsList[(digestBytes[ndx] >> 4) & 0xF];
				res[ndx*2 + 1] = HexSymbolsList[digestBytes[ndx] & 0xF];
			}
			return res;
		}

		return string (digestBytes, Sha1DigestLength * 4);
	}

	void sha1::processDigest (string_constptr input, string::size_type inputSize, char_type *outDigest) throw (std::runtime_error)
	{
		reset ();
		append (input, inputSize);

		validate ();
		calculate ();

		std::copy (&digestBytes[0], &digestBytes[0] + Sha1DigestLength * 4, outDigest);
	}

	string sha1::process (string_constref input, bool formatHex) throw (std::runtime_error)
	{
		reset ();
		append (input);

		return result(formatHex);
	}
	
	variable_type sha1::leftRotate (int bits, variable_type word) 
	{
		return ((word << bits) & 0xFFFFFFFF) | ((word & 0xFFFFFFFF) >> (32-bits));
 	}

	void sha1::calculate ()
	{
		//	Check to see if the current message block is too small to hold
		//  the initial padding bits and length.  If so, we will pad the
		//  block, process it, and then continue padding into a second
		//  block.
		
		if (messageBlockIndex > 55) // 64 - 8 - 1
		{
			messageBlock[messageBlockIndex++] = (char_type) 0x80;
			
			while(messageBlockIndex < 64)
				messageBlock[messageBlockIndex++] = 0;
			
			processMessageBlock();

			while(messageBlockIndex < 56)
				messageBlock[messageBlockIndex++] = 0;
			
		}
		else
		{
			messageBlock[messageBlockIndex++] = (char_type) 0x80;
			while(messageBlockIndex < 56)
				messageBlock[messageBlockIndex++] = 0;
			
		}

		// Store the message length as the last 8 octets
		
		messageBlock[56] = (lengthHigh >> 24) & 0xFF;
		messageBlock[57] = (lengthHigh >> 16) & 0xFF;
		messageBlock[58] = (lengthHigh >> 8) & 0xFF;
		messageBlock[59] = (lengthHigh) & 0xFF;
		messageBlock[60] = (lengthLow >> 24) & 0xFF;
		messageBlock[61] = (lengthLow >> 16) & 0xFF;
		messageBlock[62] = (lengthLow >> 8) & 0xFF;
		messageBlock[63] = (lengthLow) & 0xFF;

		processMessageBlock();

		digestBytes[3]  = (char_type) ( digest[0]       & 0xff);
		digestBytes[2]  = (char_type) ((digest[0] >> 8) & 0xff);
		digestBytes[1]  = (char_type) ((digest[0] >> 16) & 0xff);
		digestBytes[0]  = (char_type) ((digest[0] >> 24) & 0xff);

		digestBytes[7]  = (char_type) ( digest[1]       & 0xff);
		digestBytes[6]  = (char_type) ((digest[1] >> 8) & 0xff);
		digestBytes[5]  = (char_type) ((digest[1] >> 16) & 0xff);
		digestBytes[4]  = (char_type) ((digest[1] >> 24) & 0xff);

		digestBytes[11]  = (char_type) ( digest[2]       & 0xff);
		digestBytes[10]  = (char_type) ((digest[2] >> 8) & 0xff);
		digestBytes[9] = (char_type) ((digest[2] >> 16) & 0xff);
		digestBytes[8] = (char_type) ((digest[2] >> 24) & 0xff);

		digestBytes[15] = (char_type) ( digest[3]       & 0xff);
		digestBytes[14] = (char_type) ((digest[3] >> 8) & 0xff);
		digestBytes[13] = (char_type) ((digest[3] >> 16) & 0xff);
		digestBytes[12] = (char_type) ((digest[3] >> 24) & 0xff);

		digestBytes[19] = (char_type) ( digest[4]       & 0xff);
		digestBytes[18] = (char_type) ((digest[4] >> 8) & 0xff);
		digestBytes[17] = (char_type) ((digest[4] >> 16) & 0xff);
		digestBytes[16] = (char_type) ((digest[4] >> 24) & 0xff);

		computed = true;
	}

	void sha1::processMessageBlock () 
	{
		const unsigned K[] =            // Constants defined in SHA-1
		{
			0x5A827999,
			0x6ED9EBA1,
			0x8F1BBCDC,
			0xCA62C1D6
		};
		int				t;					// Loop counter
		variable_type   temp;				// Temporary word value
		variable_type	W[80] = {0};		// Word sequence
		variable_type   A, B, C, D, E;		// Word buffers

		// Initialize the first 16 words in the array W
		for(t = 0; t < 16; ++t)
		{
			W[t] = ((variable_type) messageBlock[t * 4]) << 24;
			W[t] |= ((variable_type) messageBlock[t * 4 + 1]) << 16;
			W[t] |= ((variable_type) messageBlock[t * 4 + 2]) << 8;
			W[t] |= ((variable_type) messageBlock[t * 4 + 3]);
		}

		for(t = 16; t < 80; ++t)
		{
			W[t] = leftRotate(1, W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
		}

		A = digest[0];
		B = digest[1];
		C = digest[2];
		D = digest[3];
		E = digest[4];

		for(t = 0; t < 20; ++t)
		{
			temp =  leftRotate(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
			temp &= 0xFFFFFFFF;
			E = D;
			D = C;
			C = leftRotate(30,B);
			B = A;
			A = temp;
		}

		for(t = 20; t < 40; ++t)
		{
			temp = leftRotate(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
			temp &= 0xFFFFFFFF;
			E = D;
			D = C;
			C = leftRotate(30,B);
			B = A;
			A = temp;
		}

		for(t = 40; t < 60; ++t)
		{
			temp = leftRotate(5,A) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
			temp &= 0xFFFFFFFF;
			E = D;
			D = C;
			C = leftRotate(30,B);
			B = A;
			A = temp;
		}

		for(t = 60; t < 80; ++t)
		{
			temp = leftRotate(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
			temp &= 0xFFFFFFFF;
			E = D;
			D = C;
			C = leftRotate(30,B);
			B = A;
			A = temp;
		}

		digest[0] = (digest[0] + A) & 0xFFFFFFFF;
		digest[1] = (digest[1] + B) & 0xFFFFFFFF;
		digest[2] = (digest[2] + C) & 0xFFFFFFFF;
		digest[3] = (digest[3] + D) & 0xFFFFFFFF;
		digest[4] = (digest[4] + E) & 0xFFFFFFFF;

		messageBlockIndex = 0;
	}

	

}} // namespace aconnect::crypto 

#endif // ACONNECT_CRYPTO_SHA1_H


By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Belarus Belarus
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions