A fine-tuned implementation of the SHA256 hashing algorithm.

Dependents:   EntropySource Wallet_v1

Files at this revision

API Documentation at this revision

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;