diff --git a/test/utility/SConscript b/test/utility/SConscript index b9299d29e2..e2f08ddecf 100644 --- a/test/utility/SConscript +++ b/test/utility/SConscript @@ -15,18 +15,22 @@ BOOST_LIBS = boostlibs(['unit_test_framework','system'],env) + [platform_libs] objs = env.Object('uri_boost.o', ["uri.cpp"], LIBS = BOOST_LIBS) objs += env.Object('utilities_boost.o', ["utilities.cpp"], LIBS = BOOST_LIBS) objs += env.Object('close_boost.o', ["close.cpp"], LIBS = BOOST_LIBS) +objs += env.Object('sha1_boost.o', ["sha1.cpp"], LIBS = BOOST_LIBS) prgs = env.Program('test_uri_boost', ["uri_boost.o"], LIBS = BOOST_LIBS) prgs += env.Program('test_utility_boost', ["utilities_boost.o"], LIBS = BOOST_LIBS) prgs += env.Program('test_frame', ["frame.cpp"], LIBS = BOOST_LIBS) prgs += env.Program('test_close_boost', ["close_boost.o"], LIBS = BOOST_LIBS) +prgs += env.Program('test_sha1_boost', ["sha1_boost.o"], LIBS = BOOST_LIBS) if env_cpp11.has_key('WSPP_CPP11_ENABLED'): BOOST_LIBS_CPP11 = boostlibs(['unit_test_framework'],env_cpp11) + [platform_libs] + [polyfill_libs] objs += env_cpp11.Object('utilities_stl.o', ["utilities.cpp"], LIBS = BOOST_LIBS_CPP11) objs += env_cpp11.Object('uri_stl.o', ["uri.cpp"], LIBS = BOOST_LIBS_CPP11) objs += env_cpp11.Object('close_stl.o', ["close.cpp"], LIBS = BOOST_LIBS_CPP11) + objs += env_cpp11.Object('sha1_stl.o', ["sha1.cpp"], LIBS = BOOST_LIBS_CPP11) prgs += env_cpp11.Program('test_utility_stl', ["utilities_stl.o"], LIBS = BOOST_LIBS_CPP11) prgs += env_cpp11.Program('test_uri_stl', ["uri_stl.o"], LIBS = BOOST_LIBS_CPP11) prgs += env_cpp11.Program('test_close_stl', ["close_stl.o"], LIBS = BOOST_LIBS_CPP11) + prgs += env_cpp11.Program('test_sha1_stl', ["sha1_stl.o"], LIBS = BOOST_LIBS_CPP11) Return('prgs') diff --git a/test/utility/sha1.cpp b/test/utility/sha1.cpp new file mode 100644 index 0000000000..4f43bdde37 --- /dev/null +++ b/test/utility/sha1.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2011, Peter Thorson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +//#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MODULE sha1 +#include + +#include +#include + +#include +#include + +BOOST_AUTO_TEST_SUITE ( sha1 ) + +BOOST_AUTO_TEST_CASE( sha1_test_a ) { + websocketpp::sha1 sha; + uint32_t digest[5]; + + sha << "abc"; + BOOST_CHECK(sha.get_raw_digest(digest)); + + BOOST_CHECK_EQUAL( digest[0], 0xa9993e36 ); + BOOST_CHECK_EQUAL( digest[1], 0x4706816a ); + BOOST_CHECK_EQUAL( digest[2], 0xba3e2571 ); + BOOST_CHECK_EQUAL( digest[3], 0x7850c26c ); + BOOST_CHECK_EQUAL( digest[4], 0x9cd0d89d ); +} + +BOOST_AUTO_TEST_CASE( sha1_test_b ) { + websocketpp::sha1 sha; + uint32_t digest[5]; + + sha << "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; + BOOST_CHECK(sha.get_raw_digest(digest)); + + BOOST_CHECK_EQUAL( digest[0], 0x84983e44 ); + BOOST_CHECK_EQUAL( digest[1], 0x1c3bd26e ); + BOOST_CHECK_EQUAL( digest[2], 0xbaae4aa1 ); + BOOST_CHECK_EQUAL( digest[3], 0xf95129e5 ); + BOOST_CHECK_EQUAL( digest[4], 0xe54670f1 ); +} + +BOOST_AUTO_TEST_CASE( sha1_test_c ) { + websocketpp::sha1 sha; + uint32_t digest[5]; + + for (int i = 1; i <= 1000000; i++) { + sha.input('a'); + } + + BOOST_CHECK(sha.get_raw_digest(digest)); + + BOOST_CHECK_EQUAL( digest[0], 0x34aa973c ); + BOOST_CHECK_EQUAL( digest[1], 0xd4c4daa4 ); + BOOST_CHECK_EQUAL( digest[2], 0xf61eeb2b ); + BOOST_CHECK_EQUAL( digest[3], 0xdbad2731 ); + BOOST_CHECK_EQUAL( digest[4], 0x6534016f ); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/websocketpp/processors/hybi13.hpp b/websocketpp/processors/hybi13.hpp index e3f2c459fa..e84ac50a4b 100644 --- a/websocketpp/processors/hybi13.hpp +++ b/websocketpp/processors/hybi13.hpp @@ -614,12 +614,12 @@ protected: lib::error_code process_handshake_key(std::string & key) const { key.append(constants::handshake_guid); - SHA1 sha; - uint32_t message_digest[5]; + sha1 sha; + uint32_t message_digest[5]; sha << key.c_str(); - if (sha.Result(message_digest)){ + if (sha.get_raw_digest(message_digest)){ // convert sha1 hash bytes to network byte order because this sha1 // library works on ints rather than bytes for (int i = 0; i < 5; i++) { diff --git a/websocketpp/sha1/sha1.hpp b/websocketpp/sha1/sha1.hpp index 970dd6e40d..0dfeb33c38 100755 --- a/websocketpp/sha1/sha1.hpp +++ b/websocketpp/sha1/sha1.hpp @@ -28,80 +28,73 @@ #ifndef _SHA1_H_ #define _SHA1_H_ +#include + namespace websocketpp { /// Provides SHA1 hashing functionality -class SHA1 -{ - public: +class sha1 { +public: + sha1() { + reset(); + } - SHA1() { - Reset(); - } - virtual ~SHA1() {} + virtual ~sha1() {} - /* - * Re-initialize the class - */ - void Reset() { - Length_Low = 0; - Length_High = 0; - Message_Block_Index = 0; - - H[0] = 0x67452301; - H[1] = 0xEFCDAB89; - H[2] = 0x98BADCFE; - H[3] = 0x10325476; - H[4] = 0xC3D2E1F0; - - Computed = false; - Corrupted = false; + /// Re-initialize the class + void reset() { + length_low = 0; + length_high = 0; + message_block_index = 0; + + H[0] = 0x67452301; + H[1] = 0xEFCDAB89; + H[2] = 0x98BADCFE; + H[3] = 0x10325476; + H[4] = 0xC3D2E1F0; + + computed = false; + corrupted = false; + } + + /// Extract the message digest as a raw integer array + /** + * @param [out] message_digest_array Integer array to store the message in + * @return Whether or not the extraction was sucessful + */ + bool get_raw_digest(uint32_t * message_digest_array) { + if (corrupted) { + return false; } - /* - * Returns the message digest - */ - bool Result(unsigned *message_digest_array) { - int i; // Counter - - if (Corrupted) - { - return false; - } - - if (!Computed) - { - PadMessage(); - Computed = true; - } - - for(i = 0; i < 5; i++) - { - message_digest_array[i] = H[i]; - } - - return true; + if (!computed) { + pad_message(); + computed = true; } - /* - * Provide input to SHA1 - */ - void Input( const unsigned char *message_array, - unsigned length) - { - if (!length) - { - return; - } - - if (Computed || Corrupted) - { - Corrupted = true; - return; - } - - while(length-- && !Corrupted) - { + for (int i = 0; i < 5; i++) { + message_digest_array[i] = H[i]; + } + + return true; + } + + /// Provide input to SHA1 + /** + * @param [in] message_array The input bytes + * @param [in] length Length of the message array + */ + void input(unsigned char const * message_array, uint32_t length) { + if (!length) { + return; + } + + if (computed || corrupted) { + corrupted = true; + return; + } + + while (length-- && !corrupted) { // Suppresses Visual Studio code analysis for write overrun. It doesn't know the // index into Message_Block is protected by checking length. // @@ -111,246 +104,257 @@ class SHA1 #pragma warning(push) #pragma warning(suppress: 6386) #endif - Message_Block[Message_Block_Index++] = (*message_array & 0xFF); + message_block[message_block_index++] = (*message_array & 0xFF); #ifdef _MSC_VER #pragma warning(pop) #endif - - Length_Low += 8; - Length_Low &= 0xFFFFFFFF; // Force it to 32 bits - if (Length_Low == 0) - { - Length_High++; - Length_High &= 0xFFFFFFFF; // Force it to 32 bits - if (Length_High == 0) - { - Corrupted = true; // Message is too long - } - } - - if (Message_Block_Index == 64) - { - ProcessMessageBlock(); - } - - message_array++; - } - } - void Input( const char *message_array, - unsigned length) - { - Input((unsigned char *) message_array, length); - } - void Input(unsigned char message_element) - { - Input(&message_element, 1); - } - void Input(char message_element) - { - Input((unsigned char *) &message_element, 1); - } - SHA1& operator<<(const char *message_array) - { - const char *p = message_array; - - while(*p) - { - Input(*p); - p++; - } - - return *this; - } - SHA1& operator<<(const unsigned char *message_array) - { - const unsigned char *p = message_array; - - while(*p) - { - Input(*p); - p++; - } - - return *this; - } - SHA1& operator<<(const char message_element) - { - Input((unsigned char *) &message_element, 1); - - return *this; - } - SHA1& operator<<(const unsigned char message_element) - { - Input(&message_element, 1); - - return *this; - } - - private: - - /* - * Process the next 512 bits of the message - */ - void ProcessMessageBlock() - { - const unsigned K[] = { // Constants defined for SHA-1 - 0x5A827999, - 0x6ED9EBA1, - 0x8F1BBCDC, - 0xCA62C1D6 - }; - int t; // Loop counter - unsigned temp; // Temporary word value - unsigned W[80]; // Word sequence - unsigned A, B, C, D, E; // Word buffers - - /* - * Initialize the first 16 words in the array W - */ - for(t = 0; t < 16; t++) - { - W[t] = ((unsigned) Message_Block[t * 4]) << 24; - W[t] |= ((unsigned) Message_Block[t * 4 + 1]) << 16; - W[t] |= ((unsigned) Message_Block[t * 4 + 2]) << 8; - W[t] |= ((unsigned) Message_Block[t * 4 + 3]); - } - - for(t = 16; t < 80; t++) - { - W[t] = CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); - } - - A = H[0]; - B = H[1]; - C = H[2]; - D = H[3]; - E = H[4]; - - for(t = 0; t < 20; t++) - { - temp = CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; - temp &= 0xFFFFFFFF; - E = D; - D = C; - C = CircularShift(30,B); - B = A; - A = temp; - } - - for(t = 20; t < 40; t++) - { - temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; - temp &= 0xFFFFFFFF; - E = D; - D = C; - C = CircularShift(30,B); - B = A; - A = temp; - } - - for(t = 40; t < 60; t++) - { - temp = CircularShift(5,A) + - ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; - temp &= 0xFFFFFFFF; - E = D; - D = C; - C = CircularShift(30,B); - B = A; - A = temp; - } - - for(t = 60; t < 80; t++) - { - temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; - temp &= 0xFFFFFFFF; - E = D; - D = C; - C = CircularShift(30,B); - B = A; - A = temp; - } - - H[0] = (H[0] + A) & 0xFFFFFFFF; - H[1] = (H[1] + B) & 0xFFFFFFFF; - H[2] = (H[2] + C) & 0xFFFFFFFF; - H[3] = (H[3] + D) & 0xFFFFFFFF; - H[4] = (H[4] + E) & 0xFFFFFFFF; - - Message_Block_Index = 0; - } - - /* - * Pads the current message block to 512 bits - */ - void PadMessage() - { - /* - * 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 (Message_Block_Index > 55) - { - Message_Block[Message_Block_Index++] = 0x80; - while(Message_Block_Index < 64) - { - Message_Block[Message_Block_Index++] = 0; - } - - ProcessMessageBlock(); - - while(Message_Block_Index < 56) - { - Message_Block[Message_Block_Index++] = 0; - } - } - else - { - Message_Block[Message_Block_Index++] = 0x80; - while(Message_Block_Index < 56) - { - Message_Block[Message_Block_Index++] = 0; - } - - } - - /* - * Store the message length as the last 8 octets - */ - Message_Block[56] = (Length_High >> 24) & 0xFF; - Message_Block[57] = (Length_High >> 16) & 0xFF; - Message_Block[58] = (Length_High >> 8) & 0xFF; - Message_Block[59] = (Length_High) & 0xFF; - Message_Block[60] = (Length_Low >> 24) & 0xFF; - Message_Block[61] = (Length_Low >> 16) & 0xFF; - Message_Block[62] = (Length_Low >> 8) & 0xFF; - Message_Block[63] = (Length_Low) & 0xFF; - - ProcessMessageBlock(); - } - - /* - * Performs a circular left shift operation - */ - inline unsigned CircularShift(int bits, unsigned word) - { - return ((word << bits) & 0xFFFFFFFF) | ((word & 0xFFFFFFFF) >> (32-bits)); - } - - unsigned H[5]; // Message digest buffers - - unsigned Length_Low; // Message length in bits - unsigned Length_High; // Message length in bits - - unsigned char Message_Block[64]; // 512-bit message blocks - int Message_Block_Index; // Index into message block array - - bool Computed; // Is the digest computed? - bool Corrupted; // Is the message digest corruped? + length_low += 8; + length_low &= 0xFFFFFFFF; // Force it to 32 bits + if (length_low == 0) { + length_high++; + length_high &= 0xFFFFFFFF; // Force it to 32 bits + if (length_high == 0) { + corrupted = true; // Message is too long + } + } + + if (message_block_index == 64) { + process_message_block(); + } + + message_array++; + } + } + + /// Provide input to SHA1 + /** + * Overload with signed message array + * + * @param [in] message_array The input bytes + * @param [in] length Length of the message array + */ + void input(char const * message_array, uint32_t length) { + input(reinterpret_cast(message_array), length); + } + + /// Provide input to SHA1 + /** + * Overload with a single unsigned char + * + * @param [in] message_element The character to input + */ + void input(unsigned char message_element) { + input(&message_element, 1); + } + + /// Provide input to SHA1 + /** + * Overload with a single signed char + * + * @param [in] message_element The character to input + */ + void input(char message_element) { + input(reinterpret_cast(&message_element), 1); + } + + /// Provide input to SHA1 + /** + * Overload via signed char array stream input + * + * @param [in] message_array The character array to input + */ + sha1& operator<<(char const * message_array) { + char const * p = message_array; + + while(*p) { + input(*p); + p++; + } + + return *this; + } + + /// Provide input to SHA1 + /** + * Overload via unsigned char array stream input + * + * @param [in] message_array The character array to input + */ + sha1& operator<<(unsigned char const * message_array) { + unsigned char const * p = message_array; + + while(*p) { + input(*p); + p++; + } + + return *this; + } + + /// Provide input to SHA1 + /** + * Overload via signed char stream input + * + * @param [in] message_element The character to input + */ + sha1& operator<<(char message_element) { + input((unsigned char *) &message_element, 1); + + return *this; + } + + /// Provide input to SHA1 + /** + * Overload via unsigned char stream input + * + * @param [in] message_element The character to input + */ + sha1& operator<<(unsigned char message_element) { + input(&message_element, 1); + + return *this; + } +private: + /// Process the next 512 bits of the message + void process_message_block() { + // Constants defined for SHA-1 + uint32_t const K[] = { 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 }; + uint32_t temp; // Temporary word value + uint32_t W[80]; // Word sequence + uint32_t A, B, C, D, E; // Word buffers + + // Initialize the first 16 words in the array W + for (int t = 0; t < 16; t++) { + W[t] = ((uint32_t) message_block[t * 4]) << 24; + W[t] |= ((uint32_t) message_block[t * 4 + 1]) << 16; + W[t] |= ((uint32_t) message_block[t * 4 + 2]) << 8; + W[t] |= ((uint32_t) message_block[t * 4 + 3]); + } + + for (int t = 16; t < 80; t++) { + W[t] = CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = H[0]; + B = H[1]; + C = H[2]; + D = H[3]; + E = H[4]; + + for (int t = 0; t < 20; t++) { + temp = CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = CircularShift(30,B); + B = A; + A = temp; + } + + for (int t = 20; t < 40; t++) { + temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = CircularShift(30,B); + B = A; + A = temp; + } + + for (int t = 40; t < 60; t++) { + temp = CircularShift(5,A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = CircularShift(30,B); + B = A; + A = temp; + } + + for (int t = 60; t < 80; t++) { + temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = CircularShift(30,B); + B = A; + A = temp; + } + + H[0] = (H[0] + A) & 0xFFFFFFFF; + H[1] = (H[1] + B) & 0xFFFFFFFF; + H[2] = (H[2] + C) & 0xFFFFFFFF; + H[3] = (H[3] + D) & 0xFFFFFFFF; + H[4] = (H[4] + E) & 0xFFFFFFFF; + + message_block_index = 0; + } + + /// Pads the current message block to 512 bits + void pad_message() { + // 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 (message_block_index > 55) { + message_block[message_block_index++] = 0x80; + while(message_block_index < 64) { + message_block[message_block_index++] = 0; + } + + process_message_block(); + + while(message_block_index < 56) { + message_block[message_block_index++] = 0; + } + } else { + message_block[message_block_index++] = 0x80; + while(message_block_index < 56) { + message_block[message_block_index++] = 0; + } + + } + + // Store the message length as the last 8 octets + message_block[56] = (length_high >> 24) & 0xFF; + message_block[57] = (length_high >> 16) & 0xFF; + message_block[58] = (length_high >> 8) & 0xFF; + message_block[59] = (length_high) & 0xFF; + message_block[60] = (length_low >> 24) & 0xFF; + message_block[61] = (length_low >> 16) & 0xFF; + message_block[62] = (length_low >> 8) & 0xFF; + message_block[63] = (length_low) & 0xFF; + + process_message_block(); + } + + /// Performs a circular left shift operation + /** + * @param [in] bits How many bits to shift + * @param [in] word The word to shift + * @return The shifted word + */ + inline uint32_t CircularShift(int bits, uint32_t word) { + return ((word << bits) & 0xFFFFFFFF) | ((word & 0xFFFFFFFF) >> (32-bits)); + } + + uint32_t H[5]; // Message digest buffers + + uint32_t length_low; // Message length in bits + uint32_t length_high; // Message length in bits + + unsigned char message_block[64]; // 512-bit message blocks + int message_block_index; // Index into message block array + + bool computed; // Is the digest computed? + bool corrupted; // Is the message digest corruped? + }; } // namespace websocketpp - + #endif // _SHA1_H_