minor refactoring of sha1 code

Update code style to better match project. Add more documentation. Add
some C++ specific features to improve compatibility and reduce warnings
with C++ applications. Add original project unit tests to WebSocket++
test suite.
This commit is contained in:
Peter Thorson
2013-07-14 10:34:47 -05:00
parent b8af39fd82
commit 1c199aca6e
4 changed files with 395 additions and 303 deletions

View File

@@ -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')

84
test/utility/sha1.cpp Normal file
View File

@@ -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 <boost/test/unit_test.hpp>
#include <iostream>
#include <string>
#include <websocketpp/sha1/sha1.hpp>
#include <websocketpp/utilities.hpp>
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()

View File

@@ -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++) {

View File

@@ -28,80 +28,73 @@
#ifndef _SHA1_H_
#define _SHA1_H_
#include <websocketpp/common/stdint.hpp>
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<const unsigned char *>(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<unsigned char *>(&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_