A fine-tuned implementation of the SHA256 hashing algorithm.
Dependents: EntropySource Wallet_v1
Revision 3:f19b10394f9c, committed 2011-06-20
- Comitter:
- Remco
- Date:
- Mon Jun 20 11:00:20 2011 +0000
- Parent:
- 2:1991439ea6b8
- Child:
- 4:81678751d669
- Commit message:
Changed in this revision
SHA256.cpp | Show annotated file Show diff for this revision Revisions of this file |
SHA256.h | Show annotated file Show diff for this revision Revisions of this file |
--- a/SHA256.cpp Mon Jun 20 09:45:02 2011 +0000 +++ b/SHA256.cpp Mon Jun 20 11:00:20 2011 +0000 @@ -6,11 +6,8 @@ #include "SHA256.h" -inline unsigned int byte_swap(unsigned int x) +inline unsigned int reverse_bytes(unsigned int x) { - // unsigned int result; - // asm("REV %0, %1" : "=r"(result) : "r"(x)); - // return result return __rev(x); } @@ -35,51 +32,49 @@ void SHA256::append(const char* data, int size) { - /* - unsigned int* dataw = reinterpret_cast<unsigned int*>(data); + int index = length % 64; + length += size; + const char* end = data + size; - while(length & 0x3) { - if(data == end) { - length += size; - return; - } - buffer[3 + (length & (~0x3)) - (length & 0x3)] = *data; - ++length; - ++data; + // Word align data + char* bytes = reinterpret_cast<char*>(w + (index / 4)); + switch(index % 4) + { + // Remember to reverse! (little endian!) + case 1: bytes[2] = *data++; ++index; + case 2: bytes[1] = *data++; ++index; + case 3: bytes[0] = *data++; ++index; + case 0: break; } - if(length % 64 == 0) { - process_chunk(); + if(data > end) { + // We have overshot reading data + // but w and length are correct + return; } - // Process words - int num_words = (end - data) / 4; - int index = (length % 64) / 4; - unsigned int* data_words = reinterpret_cast<unsigned int*>(data); - while(num_words--) - { - if(index == 16) { - process_chunk(); - } + // Index is now word alligned + index /= 4; + if(index == 16) { + process_chunk(); + index = 0; } - */ - const char* end = data + size; - char* buffer = reinterpret_cast<char*>(w); - - int index = length % 64; - while(data != end) { - int word_index = index / 4; - int byte_index = index % 4; - buffer[4 * word_index + 3 - byte_index] = *data; - ++index; - ++data; - if(index == 64) { + // Process whole words + int num_words = (end - data) / 4; + const unsigned int* data_words = reinterpret_cast<const unsigned int*>(data); + const unsigned int* data_words_end = data_words + num_words; + while(data_words != data_words_end) + { + w[index++] = reverse_bytes(*data_words++); + if(index == 16) { process_chunk(); index = 0; } } - length += size; + // Process trailing data bytes + // Again, we won't worry about overshooting data + w[index] = reverse_bytes(*data_words); } void SHA256::finalize() @@ -93,7 +88,7 @@ // Set all other bits to zero w[last_block] &= ~(bit_in_block - 1); - for(int i = last_block + 1; i < 15; ++i) + for(int i = last_block + 1; i < 16; ++i) w[i] = 0; // Make room for the length if necessary @@ -110,7 +105,7 @@ // Convert the result to big endian for(int i = 0; i < 8; ++i) - hash[i] = byte_swap(hash[i]); + hash[i] = reverse_bytes(hash[i]); } #define s0(x) (rotate_right(x, 7) ^ rotate_right(x, 18) ^ (x >> 3)) @@ -214,4 +209,13 @@ hash[7] += h; } - +std::string SHA256::hexString() +{ + const char* hex = "0123456789abcdef"; + std::string hexstr(64, '0'); + for(int i = 0; i < 32; ++i) { + hexstr[2 * i + 0] = hex[digest()[i] >> 4]; + hexstr[2 * i + 1] = hex[digest()[i] & 0xf]; + } + return hexstr; +}
--- a/SHA256.h Mon Jun 20 09:45:02 2011 +0000 +++ b/SHA256.h Mon Jun 20 11:00:20 2011 +0000 @@ -2,20 +2,74 @@ // Based on: // http://en.wikipedia.org/wiki/SHA-2 // http://www.iwar.org.uk/comsec/resources/cipher/sha256-384-512.pdf +// OpenSSL optimizations +#pragma once #include <string.h> #include <string> +/// Class to quickly compute SHA-256 hashes +/// +/// This class has been heavily optimized for speed +/// at a slight expense of code size. class SHA256 { public: + //// Create a new instance, ready for appending SHA256() { reset(); } + + /// Reset this instance so you can calculate a new hash void reset(); + + /// Append data to the hash + /// + /// Note: due to word-allignment optimizations + /// the function may read up to three bytes beyond + /// the end of data. + /// + /// @param data The bytes to be added. + /// @param size The number of bytes to read from data, but see the note. void append(const char* data, int size); + + /// Append a single byte. + /// Avoid this function if performance is important. + void append(char c) { append(&c, 1); } + + /// Append a zero terminated string + /// The terminating zero itself is not appended. void append(const char* str) { append(str, strlen(str)); } + + /// Append a std::string void append(const std::string& str) { append(str.data(), str.length()); } + + /// Append the required padding and compute the final digest + /// Always call this function first before requesting the digest + /// + /// After finalization you must call reset() before you can append again. + /// However, you can do this: + /// SHA256 hash1, hash1and2; + /// hash1.append("First part"); + /// hash1and2 = hash1; + /// hash1.finalize(); + /// hash1and2.append("Second part"); + /// hash1and2.finalize(); void finalize(); + + /// Returns a pointer to the 32 bytes of the digest const char* digest() { return reinterpret_cast<char*>(hash); } - std::string rawdigest() { return std::string(digest(), 32); } + + /// Return the digest as a binary std::string + /// + /// Avoid this function if performance is important, + /// the std::string constructor will make a copy of the + /// digest internally, doing a malloc and memcpy. + std::string binString() { return std::string(digest(), 32); } + + /// Return the digest as a hexadecimal std::string + /// + /// In addition to the note for binaryString, this function + /// also does conversion. + std::string hexString(); + private: void process_chunk(); int length;