cfad47cfa3/t3compiler/tads3/test/data/sha.t

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
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