| | 1 | /* |
| | 2 | * Copyright 2004 Michael J. Roberts. |
| | 3 | * |
| | 4 | * This is an implementation of the Secure Hash Algorithm-1 (SHA-1). We |
| | 5 | * provide a simple function that can be called with a string value to |
| | 6 | * calculate the SHA hash of the string. The result of hashing any |
| | 7 | * message is a string of 160 bits, which we represent as a list of five |
| | 8 | * 32-bit integer values. |
| | 9 | * |
| | 10 | * SHA is a cryptographic hash that's useful for digital signatures and |
| | 11 | * other applications. The algorithm is designed to have the properties |
| | 12 | * necessary for a good cryptographic hash; specifically, that the |
| | 13 | * function is irreversible (that is, there exists no function R(x) such |
| | 14 | * that R(H(x)) == x for arbitrary values x), that it is highly |
| | 15 | * non-linear (that is, given a value y that is "near" x or otherwise |
| | 16 | * related to x, H(y) has no detectable relationship to H(x)), and that |
| | 17 | * the probability of a collision in the hash value is very small (that |
| | 18 | * is, given x, it is intractable to find another value y such that H(y) |
| | 19 | * == H(x)). It's difficult to prove that these properties hold for any |
| | 20 | * given function, but SHA is a public algorithm that's been analyzed by |
| | 21 | * many experts, and it is widely considered to satisfy these |
| | 22 | * requirements. |
| | 23 | * |
| | 24 | * Refer to http://www.itl.nist.gov/fipspubs/fip180-1.htm for details of |
| | 25 | * the standard. |
| | 26 | */ |
| | 27 | |
| | 28 | #include <tads.h> |
| | 29 | #include <bytearr.h> |
| | 30 | #include <charset.h> |
| | 31 | |
| | 32 | /* |
| | 33 | * Calculate the SHA value for the given message string. The result is |
| | 34 | * returned as a list of five (32-bit) integers; taken together, these |
| | 35 | * give the 160-bit hash value. Note that, by design of the standard, |
| | 36 | * the hash value is always 160 bits, regardless of the input message |
| | 37 | * length. |
| | 38 | */ |
| | 39 | calcSHA(str) |
| | 40 | { |
| | 41 | local msg; |
| | 42 | local msgLen; |
| | 43 | local paddedLen; |
| | 44 | local h0 = 0x67452301; |
| | 45 | local h1 = 0xefcdab89; |
| | 46 | local h2 = 0x98badcfe; |
| | 47 | local h3 = 0x10325476; |
| | 48 | local h4 = 0xc3d2e1f0; |
| | 49 | local w = new Vector(80); |
| | 50 | local val; |
| | 51 | local a, b, c, d, e, temp; |
| | 52 | local t; |
| | 53 | local i, j; |
| | 54 | local k = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6]; |
| | 55 | local f; |
| | 56 | |
| | 57 | /* convert the message string to a byte array */ |
| | 58 | msg = str.mapToByteArray(new CharacterSet('us-ascii')); |
| | 59 | |
| | 60 | /* |
| | 61 | * We need to extend this to a 512-bit (64-byte) multiple, including |
| | 62 | * padding at the end. The padding is a minimum of 9 extra bytes, |
| | 63 | * because we have to add a '10000000' (0x80) byte and and 8-byte |
| | 64 | * length suffix. So, compute the byte length including the minimum |
| | 65 | * 9 extra bytes, and round up to the next multiple of 64. |
| | 66 | */ |
| | 67 | msgLen = msg.length(); |
| | 68 | paddedLen = (msgLen + 9 + 63) & ~63; |
| | 69 | |
| | 70 | /* allocate the larger padded array */ |
| | 71 | msg = new ByteArray(msg, 1, paddedLen); |
| | 72 | |
| | 73 | /* add the padding: start with the 0x80 byte */ |
| | 74 | msg[msgLen + 1] = 0x80; |
| | 75 | |
| | 76 | /* |
| | 77 | * add the 0x00 bytes - we need to fill the space we added beyond the |
| | 78 | * fixed parts (the 0x00 byte and length suffix), which add up to 9 |
| | 79 | * characters in all, so this is simply the padded length minus the |
| | 80 | * message length minus 9 |
| | 81 | */ |
| | 82 | msg.fillValue(0, msgLen + 2, paddedLen - msgLen - 9); |
| | 83 | |
| | 84 | /* add the length suffix (in *bits*), as a big-endian value */ |
| | 85 | msg.writeInt(paddedLen - 7, FmtInt32BE, 0); |
| | 86 | msg.writeInt(paddedLen - 3, FmtInt32BE, msgLen * 8); |
| | 87 | |
| | 88 | /* now process the message */ |
| | 89 | for (i = 1 ; i <= paddedLen ; i += 64) |
| | 90 | { |
| | 91 | /* copy the message words into the w array */ |
| | 92 | for (j = 0 ; j < 16 ; ++j) |
| | 93 | w[j+1] = msg.readInt(i + j*4, FmtInt32BE); |
| | 94 | |
| | 95 | /* calculate w's 16-79 */ |
| | 96 | for (t = 17 ; t <= 80 ; ++t) |
| | 97 | { |
| | 98 | val = w[t-3] ^ w[t-8] ^ w[t-14] ^ w[t-16]; |
| | 99 | w[t] = shaROL(val, 1); |
| | 100 | } |
| | 101 | |
| | 102 | /* calculate the a-e values */ |
| | 103 | a = h0; |
| | 104 | b = h1; |
| | 105 | c = h2; |
| | 106 | d = h3; |
| | 107 | e = h4; |
| | 108 | |
| | 109 | /* iteratively calculate the new a-e values */ |
| | 110 | for (t = 1 ; t <= 80 ; ++t) |
| | 111 | { |
| | 112 | /* calculate the f[t] value */ |
| | 113 | if (t <= 20) |
| | 114 | f = (b & c) | (~b & d); |
| | 115 | else if (t <= 40 || t > 60) |
| | 116 | f = b ^ c ^ d; |
| | 117 | else if (t <= 60) |
| | 118 | f = (b & c) | (b & d) | (c & d); |
| | 119 | |
| | 120 | /* calculate the new a-e values for this round */ |
| | 121 | temp = shaROL(a, 5) + f + e + w[t] + k[(t-1)/20 + 1]; |
| | 122 | e = d; |
| | 123 | d = c; |
| | 124 | c = shaROL(b, 30); |
| | 125 | b = a; |
| | 126 | a = temp; |
| | 127 | } |
| | 128 | |
| | 129 | /* calculate the new h's */ |
| | 130 | h0 += a; |
| | 131 | h1 += b; |
| | 132 | h2 += c; |
| | 133 | h3 += d; |
| | 134 | h4 += e; |
| | 135 | } |
| | 136 | |
| | 137 | /* the digest is now the h's - return it as a list */ |
| | 138 | return [h0, h1, h2, h3, h4]; |
| | 139 | } |
| | 140 | |
| | 141 | /* SHA service routine - rotate 'val' left by 'b' bits */ |
| | 142 | shaROL(val, b) |
| | 143 | { |
| | 144 | /* |
| | 145 | * Shift the value left by the given number of bits, and OR in the |
| | 146 | * result of shifting it right by the complementary amount (32 minus |
| | 147 | * the number of bits). Mask off any high-order bits filled in for |
| | 148 | * us in the right-shift; if the value is signed, those will show up. |
| | 149 | */ |
| | 150 | return (val << b) | ((val >> (32 - b)) & ((1 << b) - 1)); |
| | 151 | } |
| | 152 | |
| | 153 | /* ------------------------------------------------------------------------ */ |
| | 154 | /* |
| | 155 | * Test main: display the hash value of a string passed as the |
| | 156 | * command-line argument to the program. |
| | 157 | */ |
| | 158 | #ifdef TEST_SHA |
| | 159 | |
| | 160 | main(args) |
| | 161 | { |
| | 162 | local hash; |
| | 163 | |
| | 164 | /* check arguments */ |
| | 165 | if (args.length() < 2) |
| | 166 | { |
| | 167 | "No message specified.\n"; |
| | 168 | return; |
| | 169 | } |
| | 170 | |
| | 171 | /* calculate the hash */ |
| | 172 | hash = calcSHA(args[2]); |
| | 173 | |
| | 174 | /* display the result - this is a list of five integer values */ |
| | 175 | "Digest = <<toString(hash[1], 16)>> <<toString(hash[2], 16)>> |
| | 176 | <<toString(hash[3], 16)>> <<toString(hash[4], 16)>> |
| | 177 | <<toString(hash[5], 16)>>\n"; |
| | 178 | } |
| | 179 | |
| | 180 | #endif /* TEST_SHA */ |
| | 181 | |