summaryrefslogtreecommitdiff
path: root/src/utils/sha2.h
blob: 79f6b862297491c376f1f594c6db26464df7b287 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/*************************************************************

    This program is a C++ implementation of the Secure Hash Algorithm (SHA)
    that handles the variations from the original 160 bit to 224, 256, 384
    and 512 bit.  The program is intended to be platform independant and
    has been tested on little-endian (Intel) and big-endian (Sun) machines.

    This program is based on a C version written by Aaron D. Gifford
    (as of 11/22/2004 his code could be found at http://www.adg.us/computers/sha.html).
    Attempts to contact him were unsuccessful.  I greatly condensed his version
    and shared as much code and data as I could think of.  I also inlined
    a lot of code that were macros in his version.  My version detects
    endian-ness automatically and adjusts itself accordingly.  This program
    has been tested with Visual C++ versions 6/7 and Dev-C++ on Windows, 
    g++ on Linux and CC on Solaris (g++ on Solaris gave a bus error).

    While I did make half-hearted attempts to optimize as I went along
    (testing on Wintel), any serious attempt at fast implementation is
    probably going to need to make use of in-lined assembly which is not
    very portable.

    The goal of this implementation is ease of use.  As much as possible
    I tried to hide implementation details while making it trivial to change
    the size of the hash and get the results.  The string and charactar
    array value of the hash is supplied as human-readable hex; the raw value
    can also be obtained.

    If you use this implementation somewhere I would like to be credited
    with my work (a link to my page below is fine).  I add no license
    restriction beyond any that is made by the original author.  This
    code comes with no warrenty expressed or implied, use at your own
    risk!

    Keith Oxenrider
    koxenrider[at]sol[dash]biotech[dot]com
    The latest version of this code should be available via the page
    sol-biotech.com/code.

*************************************************************/

#ifndef _TMW_UTILS_SHA2C_H
#define _TMW_UTILS_SHA2C_H

#include <string>
#include <stdexcept>

// NOTE: You may need to define things by hand for your system: 
typedef unsigned char  sha_byte;           // Exactly 1 byte 
typedef unsigned int sha_word32;           // Exactly 4 bytes 
#ifdef WIN32
    #include <windows.h>
    typedef ULONG64 sha_word64;            // 8-bytes (64-bits) 
#else
    typedef unsigned long long sha_word64; // 8-bytes (64-bits) 
#endif

// Digest lengths for SHA-1/224/256/384/512 
const sha_word32 SHA1_DIGESTC_LENGTH = 20;
const sha_word32 SHA1_DIGESTC_STRING_LENGTH   = (SHA1_DIGESTC_LENGTH   * 2 + 1);
const sha_word32 SHA224_DIGESTC_LENGTH = 28;
const sha_word32 SHA224_DIGESTC_STRING_LENGTH = (SHA224_DIGESTC_LENGTH * 2 + 1);
const sha_word32 SHA256_DIGESTC_LENGTH = 32;
const sha_word32 SHA256_DIGESTC_STRING_LENGTH = (SHA256_DIGESTC_LENGTH * 2 + 1);
const sha_word32 SHA384_DIGESTC_LENGTH = 48;
const sha_word32 SHA384_DIGESTC_STRING_LENGTH = (SHA384_DIGESTC_LENGTH * 2 + 1);
const sha_word32 SHA512_DIGESTC_LENGTH = 64;
const sha_word32 SHA512_DIGESTC_STRING_LENGTH = (SHA512_DIGESTC_LENGTH * 2 + 1);

class sha2{
public:
    enum SHA_TYPE{
        enuSHA_NONE,
        enuSHA1,
        enuSHA160 = enuSHA1,
        enuSHA224,
        enuSHA256,
        enuSHA384,
        enuSHA512,
        enuSHA_LAST //for easier looping during testing
    };

    sha2(){
        m_Type = enuSHA_NONE;
        m_boolIsBigEndian = true;
        m_boolEnded = false;

        //run-time check for endian-ness
        unsigned int test = 1;
        unsigned char *ptr = (unsigned char *)&test;
        if (ptr[0]) m_boolIsBigEndian = false;

        //these checks here because I wasn't able to figure out how to 
        //check at compile time
        if (sizeof(sha_byte) != 1) throw std::runtime_error("sha_byte != 1!");
        if (sizeof(sha_word32) != 4) throw std::runtime_error("sha_word32 != 4!");
        if (sizeof(sha_word64) != 8) throw std::runtime_error("sha_word64 != 8!");

        memset(m_chrRawHash, 0, SHA512_DIGESTC_LENGTH);
        memset(m_chrHexHash, 0, SHA512_DIGESTC_STRING_LENGTH);
        memset(m_digest, 0, SHA512_DIGESTC_LENGTH);
    };

    SHA_TYPE GetEnumType(){return m_Type;};
    bool IsBigEndian(){return m_boolIsBigEndian;};
    const char * GetTypeString(){
        switch (m_Type){
            case sha2::enuSHA1   : return "SHA160";
            case sha2::enuSHA224 : return "SHA224";
            case sha2::enuSHA256 : return "SHA256";
            case sha2::enuSHA384 : return "SHA384";
            case sha2::enuSHA512 : return "SHA512";
            default : return "Unknown!";
        }
    };

//call these three in order if you want to load chunk-by-chunk...
    void Init(SHA_TYPE type);
    //these two throw a std::runtime_error if the type is not defined
    void Update(const sha_byte *data, size_t len);//call as many times as needed
    void End();

//or call this one if you only have one chunk of data
    const std::string &GetHash(SHA_TYPE type, const sha_byte* data, size_t len);

//call one of these routines to access the hash
    //these throw a std::runtime_error if End has not been called
    const char *HexHash();//NULL terminated
    const std::string &StringHash();
    const char *RawHash(int &length);//NO NULL termination! size stored in 'length'


private:
    SHA_TYPE m_Type;
    std::string m_strHash;
    bool m_boolEnded, m_boolIsBigEndian;
    char m_chrRawHash[SHA512_DIGESTC_LENGTH], m_chrHexHash[SHA512_DIGESTC_STRING_LENGTH];
    sha_byte m_digest[SHA512_DIGESTC_LENGTH];

//these are common buffers for maintaining the hash
    struct SHA_CTX{
        sha_byte   state[sizeof(sha_word64) * 8];//maximum size
        sha_word64 bitcount[2];//sha1, 224 and 256 only use the first entry
        sha_byte   buffer[128];
    }ctx;

    
//** INTERNAL FUNCTION PROTOTYPES ************************************
    void SHA256_Internal_Last(bool isSha1 = false);
    void SHA512_Internal_Last();

    void SHA1_Internal_Transform(const sha_word32 *data);
    void SHA256_Internal_Transform(const sha_word32* data);
    void SHA512_Internal_Transform(const sha_word64*);

    void SHA32bit_Update(const sha_byte *data, size_t len, bool isSha1=false);
    void SHA64bit_Update(const sha_byte *data, size_t len);

//macro replacements
    inline void MEMSET_BZERO(void *p, size_t l){memset(p, 0, l);};
    inline void MEMCPY_BCOPY(void *d,const void *s, size_t l) {memcpy(d, s, l);};

    //For incrementally adding the unsigned 64-bit integer n to the
    //unsigned 128-bit integer (represented using a two-element array of
    //64-bit words):
    inline void ADDINC128(sha_word64 *w, sha_word32 n)  {
        w[0] += (sha_word64)(n);
        if (w[0] < (n)) w[1]++;
    }

    // Shift-right (used in SHA-256, SHA-384, and SHA-512): 
    inline sha_word32 SHR(sha_word32 b,sha_word32 x){return (x >> b);};
    inline sha_word64 SHR(sha_word64 b,sha_word64 x){return (x >> b);};
    // 32-bit Rotate-right (used in SHA-256): 
    inline sha_word32 ROTR32(sha_word32 b,sha_word32 x){return ((x >> b) | (x << (32 - b)));};
    // 64-bit Rotate-right (used in SHA-384 and SHA-512): 
    inline sha_word64 ROTR64(sha_word64 b,sha_word64 x){return ((x >> b) | (x << (64 - b)));};
    // 32-bit Rotate-left (used in SHA-1): 
    inline sha_word32 ROTL32(sha_word32 b,sha_word32 x){return ((x << b) | (x >> (32 - b)));};

    // Two logical functions used in SHA-1, SHA-254, SHA-256, SHA-384, and SHA-512: 
    inline sha_word32 Ch(sha_word32 x,sha_word32 y,sha_word32 z){return ((x & y) ^ ((~x) & z));};
    inline sha_word64 Ch(sha_word64 x,sha_word64 y,sha_word64 z){return ((x & y) ^ ((~x) & z));};
    inline sha_word32 Maj(sha_word32 x,sha_word32 y,sha_word32 z){return ((x & y) ^ (x & z) ^ (y & z));};
    inline sha_word64 Maj(sha_word64 x,sha_word64 y,sha_word64 z){return ((x & y) ^ (x & z) ^ (y & z));};

    // Function used in SHA-1: 
    inline sha_word32 Parity(sha_word32 x,sha_word32 y,sha_word32 z){return (x ^ y ^ z);};

// Four logical functions used in SHA-256: 
    inline sha_word32 Sigma0_256(sha_word32 x){return (ROTR32(2, x) ^ ROTR32(13, x) ^ ROTR32(22, x));};
    inline sha_word32 Sigma1_256(sha_word32 x){return (ROTR32(6, x) ^ ROTR32(11, x) ^ ROTR32(25, x));};
    inline sha_word32 sigma0_256(sha_word32 x){return (ROTR32(7, x) ^ ROTR32(18, x) ^ SHR(   3 , x));};
    inline sha_word32 sigma1_256(sha_word32 x){return (ROTR32(17,x) ^ ROTR32(19, x) ^ SHR(   10, x));};

// Four of six logical functions used in SHA-384 and SHA-512: 
    inline sha_word64 Sigma0_512(sha_word64 x){return (ROTR64(28, x) ^ ROTR64(34, x) ^ ROTR64(39, x));};
    inline sha_word64 Sigma1_512(sha_word64 x){return (ROTR64(14, x) ^ ROTR64(18, x) ^ ROTR64(41, x));};
    inline sha_word64 sigma0_512(sha_word64 x){return (ROTR64( 1, x) ^ ROTR64( 8, x) ^ SHR(    7, x));};
    inline sha_word64 sigma1_512(sha_word64 x){return (ROTR64(19, x) ^ ROTR64(61, x) ^ SHR(    6, x));};

    inline void REVERSE32(sha_word32 w, sha_word32 &x)  {
        w = (w >> 16) | (w << 16);
        x = ((w & 0xff00ff00UL) >> 8) | ((w & 0x00ff00ffUL) << 8);
    }
    #ifdef _VC6
        inline void REVERSE64(sha_word64 w, sha_word64 &x)  {
            w = (w >> 32) | (w << 32);
            w =   ((w & 0xff00ff00ff00ff00ui64) >> 8) |
                  ((w & 0x00ff00ff00ff00ffui64) << 8);
            (x) = ((w & 0xffff0000ffff0000ui64) >> 16) |
                  ((w & 0x0000ffff0000ffffui64) << 16);
        }
    #else
        inline void REVERSE64(sha_word64 w, sha_word64 &x)  {
            w = (w >> 32) | (w << 32);
            w =   ((w & 0xff00ff00ff00ff00ULL) >> 8) |
                  ((w & 0x00ff00ff00ff00ffULL) << 8);
            (x) = ((w & 0xffff0000ffff0000ULL) >> 16) |
                  ((w & 0x0000ffff0000ffffULL) << 16);
        }
    #endif

};//end class sha2
#endif // __SHA2C_H__