| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 1 | /* SHA256 module */ | 
 | 2 |  | 
 | 3 | /* This module provides an interface to NIST's SHA-256 and SHA-224 Algorithms */ | 
 | 4 |  | 
 | 5 | /* See below for information about the original code this module was | 
 | 6 |    based upon. Additional work performed by: | 
 | 7 |  | 
 | 8 |    Andrew Kuchling (amk@amk.ca) | 
 | 9 |    Greg Stein (gstein@lyra.org) | 
 | 10 |    Trevor Perrin (trevp@trevp.net) | 
 | 11 |  | 
| Gregory P. Smith | 2f21eb3 | 2007-09-09 06:44:34 +0000 | [diff] [blame] | 12 |    Copyright (C) 2005-2007   Gregory P. Smith (greg@krypto.org) | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 13 |    Licensed to PSF under a Contributor Agreement. | 
 | 14 |  | 
 | 15 | */ | 
 | 16 |  | 
 | 17 | /* SHA objects */ | 
 | 18 |  | 
 | 19 | #include "Python.h" | 
 | 20 | #include "structmember.h" | 
 | 21 |  | 
 | 22 |  | 
 | 23 | /* Endianness testing and definitions */ | 
 | 24 | #define TestEndianness(variable) {int i=1; variable=PCT_BIG_ENDIAN;\ | 
 | 25 | 	if (*((char*)&i)==1) variable=PCT_LITTLE_ENDIAN;} | 
 | 26 |  | 
 | 27 | #define PCT_LITTLE_ENDIAN 1 | 
 | 28 | #define PCT_BIG_ENDIAN 0 | 
 | 29 |  | 
 | 30 | /* Some useful types */ | 
 | 31 |  | 
 | 32 | typedef unsigned char SHA_BYTE; | 
 | 33 |  | 
 | 34 | #if SIZEOF_INT == 4 | 
 | 35 | typedef unsigned int SHA_INT32;	/* 32-bit integer */ | 
 | 36 | #else | 
 | 37 | /* not defined. compilation will die. */ | 
 | 38 | #endif | 
 | 39 |  | 
 | 40 | /* The SHA block size and message digest sizes, in bytes */ | 
 | 41 |  | 
 | 42 | #define SHA_BLOCKSIZE    64 | 
 | 43 | #define SHA_DIGESTSIZE  32 | 
 | 44 |  | 
 | 45 | /* The structure for storing SHA info */ | 
 | 46 |  | 
 | 47 | typedef struct { | 
 | 48 |     PyObject_HEAD | 
 | 49 |     SHA_INT32 digest[8];		/* Message digest */ | 
 | 50 |     SHA_INT32 count_lo, count_hi;	/* 64-bit bit count */ | 
 | 51 |     SHA_BYTE data[SHA_BLOCKSIZE];	/* SHA data buffer */ | 
 | 52 |     int Endianness; | 
 | 53 |     int local;				/* unprocessed amount in data */ | 
 | 54 |     int digestsize; | 
 | 55 | } SHAobject; | 
 | 56 |  | 
 | 57 | /* When run on a little-endian CPU we need to perform byte reversal on an | 
 | 58 |    array of longwords. */ | 
 | 59 |  | 
 | 60 | static void longReverse(SHA_INT32 *buffer, int byteCount, int Endianness) | 
 | 61 | { | 
 | 62 |     SHA_INT32 value; | 
 | 63 |  | 
 | 64 |     if ( Endianness == PCT_BIG_ENDIAN ) | 
 | 65 | 	return; | 
 | 66 |  | 
 | 67 |     byteCount /= sizeof(*buffer); | 
 | 68 |     while (byteCount--) { | 
 | 69 |         value = *buffer; | 
 | 70 |         value = ( ( value & 0xFF00FF00L ) >> 8  ) | \ | 
 | 71 |                 ( ( value & 0x00FF00FFL ) << 8 ); | 
 | 72 |         *buffer++ = ( value << 16 ) | ( value >> 16 ); | 
 | 73 |     } | 
 | 74 | } | 
 | 75 |  | 
 | 76 | static void SHAcopy(SHAobject *src, SHAobject *dest) | 
 | 77 | { | 
 | 78 |     dest->Endianness = src->Endianness; | 
 | 79 |     dest->local = src->local; | 
 | 80 |     dest->digestsize = src->digestsize; | 
 | 81 |     dest->count_lo = src->count_lo; | 
 | 82 |     dest->count_hi = src->count_hi; | 
 | 83 |     memcpy(dest->digest, src->digest, sizeof(src->digest)); | 
 | 84 |     memcpy(dest->data, src->data, sizeof(src->data)); | 
 | 85 | } | 
 | 86 |  | 
 | 87 |  | 
 | 88 | /* ------------------------------------------------------------------------ | 
 | 89 |  * | 
 | 90 |  * This code for the SHA-256 algorithm was noted as public domain. The | 
 | 91 |  * original headers are pasted below. | 
 | 92 |  * | 
 | 93 |  * Several changes have been made to make it more compatible with the | 
 | 94 |  * Python environment and desired interface. | 
 | 95 |  * | 
 | 96 |  */ | 
 | 97 |  | 
 | 98 | /* LibTomCrypt, modular cryptographic library -- Tom St Denis | 
 | 99 |  * | 
 | 100 |  * LibTomCrypt is a library that provides various cryptographic | 
 | 101 |  * algorithms in a highly modular and flexible manner. | 
 | 102 |  * | 
 | 103 |  * The library is free for all purposes without any express | 
 | 104 |  * gurantee it works. | 
 | 105 |  * | 
| Gregory P. Smith | 2f21eb3 | 2007-09-09 06:44:34 +0000 | [diff] [blame] | 106 |  * Tom St Denis, tomstdenis@iahu.ca, http://libtom.org | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 107 |  */ | 
 | 108 |  | 
 | 109 |  | 
 | 110 | /* SHA256 by Tom St Denis */ | 
 | 111 |  | 
 | 112 | /* Various logical functions */ | 
 | 113 | #define ROR(x, y)\ | 
 | 114 | ( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | \ | 
 | 115 | ((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) | 
 | 116 | #define Ch(x,y,z)       (z ^ (x & (y ^ z))) | 
 | 117 | #define Maj(x,y,z)      (((x | y) & z) | (x & y))  | 
 | 118 | #define S(x, n)         ROR((x),(n)) | 
 | 119 | #define R(x, n)         (((x)&0xFFFFFFFFUL)>>(n)) | 
 | 120 | #define Sigma0(x)       (S(x, 2) ^ S(x, 13) ^ S(x, 22)) | 
 | 121 | #define Sigma1(x)       (S(x, 6) ^ S(x, 11) ^ S(x, 25)) | 
 | 122 | #define Gamma0(x)       (S(x, 7) ^ S(x, 18) ^ R(x, 3)) | 
 | 123 | #define Gamma1(x)       (S(x, 17) ^ S(x, 19) ^ R(x, 10)) | 
 | 124 |  | 
 | 125 |  | 
 | 126 | static void | 
 | 127 | sha_transform(SHAobject *sha_info) | 
 | 128 | { | 
 | 129 |     int i; | 
 | 130 | 	SHA_INT32 S[8], W[64], t0, t1; | 
 | 131 |  | 
 | 132 |     memcpy(W, sha_info->data, sizeof(sha_info->data)); | 
 | 133 |     longReverse(W, (int)sizeof(sha_info->data), sha_info->Endianness); | 
 | 134 |  | 
 | 135 |     for (i = 16; i < 64; ++i) { | 
 | 136 | 		W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; | 
 | 137 |     } | 
 | 138 |     for (i = 0; i < 8; ++i) { | 
 | 139 |         S[i] = sha_info->digest[i]; | 
 | 140 |     } | 
 | 141 |  | 
 | 142 |     /* Compress */ | 
 | 143 | #define RND(a,b,c,d,e,f,g,h,i,ki)                    \ | 
 | 144 |      t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i];   \ | 
 | 145 |      t1 = Sigma0(a) + Maj(a, b, c);                  \ | 
 | 146 |      d += t0;                                        \ | 
 | 147 |      h  = t0 + t1; | 
 | 148 |  | 
 | 149 |     RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98); | 
 | 150 |     RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491); | 
 | 151 |     RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf); | 
 | 152 |     RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5); | 
 | 153 |     RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b); | 
 | 154 |     RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1); | 
 | 155 |     RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4); | 
 | 156 |     RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5); | 
 | 157 |     RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98); | 
 | 158 |     RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01); | 
 | 159 |     RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be); | 
 | 160 |     RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3); | 
 | 161 |     RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74); | 
 | 162 |     RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe); | 
 | 163 |     RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7); | 
 | 164 |     RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174); | 
 | 165 |     RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1); | 
 | 166 |     RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786); | 
 | 167 |     RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6); | 
 | 168 |     RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc); | 
 | 169 |     RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f); | 
 | 170 |     RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa); | 
 | 171 |     RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc); | 
 | 172 |     RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da); | 
 | 173 |     RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152); | 
 | 174 |     RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d); | 
 | 175 |     RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8); | 
 | 176 |     RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7); | 
 | 177 |     RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3); | 
 | 178 |     RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147); | 
 | 179 |     RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351); | 
 | 180 |     RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967); | 
 | 181 |     RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85); | 
 | 182 |     RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138); | 
 | 183 |     RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc); | 
 | 184 |     RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13); | 
 | 185 |     RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354); | 
 | 186 |     RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb); | 
 | 187 |     RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e); | 
 | 188 |     RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85); | 
 | 189 |     RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1); | 
 | 190 |     RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b); | 
 | 191 |     RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70); | 
 | 192 |     RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3); | 
 | 193 |     RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819); | 
 | 194 |     RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624); | 
 | 195 |     RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585); | 
 | 196 |     RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070); | 
 | 197 |     RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116); | 
 | 198 |     RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08); | 
 | 199 |     RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c); | 
 | 200 |     RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5); | 
 | 201 |     RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3); | 
 | 202 |     RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a); | 
 | 203 |     RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f); | 
 | 204 |     RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3); | 
 | 205 |     RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee); | 
 | 206 |     RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f); | 
 | 207 |     RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814); | 
 | 208 |     RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208); | 
 | 209 |     RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa); | 
 | 210 |     RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb); | 
 | 211 |     RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7); | 
 | 212 |     RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2); | 
 | 213 |  | 
 | 214 | #undef RND      | 
 | 215 |      | 
 | 216 |     /* feedback */ | 
 | 217 |     for (i = 0; i < 8; i++) { | 
 | 218 |         sha_info->digest[i] = sha_info->digest[i] + S[i]; | 
 | 219 |     } | 
 | 220 |  | 
 | 221 | } | 
 | 222 |  | 
 | 223 |  | 
 | 224 |  | 
 | 225 | /* initialize the SHA digest */ | 
 | 226 |  | 
 | 227 | static void | 
 | 228 | sha_init(SHAobject *sha_info) | 
 | 229 | { | 
 | 230 |     TestEndianness(sha_info->Endianness) | 
 | 231 |     sha_info->digest[0] = 0x6A09E667L; | 
 | 232 |     sha_info->digest[1] = 0xBB67AE85L; | 
 | 233 |     sha_info->digest[2] = 0x3C6EF372L; | 
 | 234 |     sha_info->digest[3] = 0xA54FF53AL; | 
 | 235 |     sha_info->digest[4] = 0x510E527FL; | 
 | 236 |     sha_info->digest[5] = 0x9B05688CL; | 
 | 237 |     sha_info->digest[6] = 0x1F83D9ABL; | 
 | 238 |     sha_info->digest[7] = 0x5BE0CD19L; | 
 | 239 |     sha_info->count_lo = 0L; | 
 | 240 |     sha_info->count_hi = 0L; | 
 | 241 |     sha_info->local = 0; | 
 | 242 |     sha_info->digestsize = 32; | 
 | 243 | } | 
 | 244 |  | 
 | 245 | static void | 
 | 246 | sha224_init(SHAobject *sha_info) | 
 | 247 | { | 
 | 248 |     TestEndianness(sha_info->Endianness) | 
 | 249 |     sha_info->digest[0] = 0xc1059ed8L; | 
 | 250 |     sha_info->digest[1] = 0x367cd507L; | 
 | 251 |     sha_info->digest[2] = 0x3070dd17L; | 
 | 252 |     sha_info->digest[3] = 0xf70e5939L; | 
 | 253 |     sha_info->digest[4] = 0xffc00b31L; | 
 | 254 |     sha_info->digest[5] = 0x68581511L; | 
 | 255 |     sha_info->digest[6] = 0x64f98fa7L; | 
 | 256 |     sha_info->digest[7] = 0xbefa4fa4L; | 
 | 257 |     sha_info->count_lo = 0L; | 
 | 258 |     sha_info->count_hi = 0L; | 
 | 259 |     sha_info->local = 0; | 
 | 260 |     sha_info->digestsize = 28; | 
 | 261 | } | 
 | 262 |  | 
 | 263 |  | 
 | 264 | /* update the SHA digest */ | 
 | 265 |  | 
 | 266 | static void | 
 | 267 | sha_update(SHAobject *sha_info, SHA_BYTE *buffer, int count) | 
 | 268 | { | 
 | 269 |     int i; | 
 | 270 |     SHA_INT32 clo; | 
 | 271 |  | 
 | 272 |     clo = sha_info->count_lo + ((SHA_INT32) count << 3); | 
 | 273 |     if (clo < sha_info->count_lo) { | 
 | 274 |         ++sha_info->count_hi; | 
 | 275 |     } | 
 | 276 |     sha_info->count_lo = clo; | 
 | 277 |     sha_info->count_hi += (SHA_INT32) count >> 29; | 
 | 278 |     if (sha_info->local) { | 
 | 279 |         i = SHA_BLOCKSIZE - sha_info->local; | 
 | 280 |         if (i > count) { | 
 | 281 |             i = count; | 
 | 282 |         } | 
 | 283 |         memcpy(((SHA_BYTE *) sha_info->data) + sha_info->local, buffer, i); | 
 | 284 |         count -= i; | 
 | 285 |         buffer += i; | 
 | 286 |         sha_info->local += i; | 
 | 287 |         if (sha_info->local == SHA_BLOCKSIZE) { | 
 | 288 |             sha_transform(sha_info); | 
 | 289 |         } | 
 | 290 |         else { | 
 | 291 |             return; | 
 | 292 |         } | 
 | 293 |     } | 
 | 294 |     while (count >= SHA_BLOCKSIZE) { | 
 | 295 |         memcpy(sha_info->data, buffer, SHA_BLOCKSIZE); | 
 | 296 |         buffer += SHA_BLOCKSIZE; | 
 | 297 |         count -= SHA_BLOCKSIZE; | 
 | 298 |         sha_transform(sha_info); | 
 | 299 |     } | 
 | 300 |     memcpy(sha_info->data, buffer, count); | 
 | 301 |     sha_info->local = count; | 
 | 302 | } | 
 | 303 |  | 
 | 304 | /* finish computing the SHA digest */ | 
 | 305 |  | 
 | 306 | static void | 
 | 307 | sha_final(unsigned char digest[SHA_DIGESTSIZE], SHAobject *sha_info) | 
 | 308 | { | 
 | 309 |     int count; | 
 | 310 |     SHA_INT32 lo_bit_count, hi_bit_count; | 
 | 311 |  | 
 | 312 |     lo_bit_count = sha_info->count_lo; | 
 | 313 |     hi_bit_count = sha_info->count_hi; | 
 | 314 |     count = (int) ((lo_bit_count >> 3) & 0x3f); | 
 | 315 |     ((SHA_BYTE *) sha_info->data)[count++] = 0x80; | 
 | 316 |     if (count > SHA_BLOCKSIZE - 8) { | 
 | 317 | 	memset(((SHA_BYTE *) sha_info->data) + count, 0, | 
 | 318 | 	       SHA_BLOCKSIZE - count); | 
 | 319 | 	sha_transform(sha_info); | 
 | 320 | 	memset((SHA_BYTE *) sha_info->data, 0, SHA_BLOCKSIZE - 8); | 
 | 321 |     } | 
 | 322 |     else { | 
 | 323 | 	memset(((SHA_BYTE *) sha_info->data) + count, 0, | 
 | 324 | 	       SHA_BLOCKSIZE - 8 - count); | 
 | 325 |     } | 
 | 326 |  | 
 | 327 |     /* GJS: note that we add the hi/lo in big-endian. sha_transform will | 
 | 328 |        swap these values into host-order. */ | 
 | 329 |     sha_info->data[56] = (hi_bit_count >> 24) & 0xff; | 
 | 330 |     sha_info->data[57] = (hi_bit_count >> 16) & 0xff; | 
 | 331 |     sha_info->data[58] = (hi_bit_count >>  8) & 0xff; | 
 | 332 |     sha_info->data[59] = (hi_bit_count >>  0) & 0xff; | 
 | 333 |     sha_info->data[60] = (lo_bit_count >> 24) & 0xff; | 
 | 334 |     sha_info->data[61] = (lo_bit_count >> 16) & 0xff; | 
 | 335 |     sha_info->data[62] = (lo_bit_count >>  8) & 0xff; | 
 | 336 |     sha_info->data[63] = (lo_bit_count >>  0) & 0xff; | 
 | 337 |     sha_transform(sha_info); | 
 | 338 |     digest[ 0] = (unsigned char) ((sha_info->digest[0] >> 24) & 0xff); | 
 | 339 |     digest[ 1] = (unsigned char) ((sha_info->digest[0] >> 16) & 0xff); | 
 | 340 |     digest[ 2] = (unsigned char) ((sha_info->digest[0] >>  8) & 0xff); | 
 | 341 |     digest[ 3] = (unsigned char) ((sha_info->digest[0]      ) & 0xff); | 
 | 342 |     digest[ 4] = (unsigned char) ((sha_info->digest[1] >> 24) & 0xff); | 
 | 343 |     digest[ 5] = (unsigned char) ((sha_info->digest[1] >> 16) & 0xff); | 
 | 344 |     digest[ 6] = (unsigned char) ((sha_info->digest[1] >>  8) & 0xff); | 
 | 345 |     digest[ 7] = (unsigned char) ((sha_info->digest[1]      ) & 0xff); | 
 | 346 |     digest[ 8] = (unsigned char) ((sha_info->digest[2] >> 24) & 0xff); | 
 | 347 |     digest[ 9] = (unsigned char) ((sha_info->digest[2] >> 16) & 0xff); | 
 | 348 |     digest[10] = (unsigned char) ((sha_info->digest[2] >>  8) & 0xff); | 
 | 349 |     digest[11] = (unsigned char) ((sha_info->digest[2]      ) & 0xff); | 
 | 350 |     digest[12] = (unsigned char) ((sha_info->digest[3] >> 24) & 0xff); | 
 | 351 |     digest[13] = (unsigned char) ((sha_info->digest[3] >> 16) & 0xff); | 
 | 352 |     digest[14] = (unsigned char) ((sha_info->digest[3] >>  8) & 0xff); | 
 | 353 |     digest[15] = (unsigned char) ((sha_info->digest[3]      ) & 0xff); | 
 | 354 |     digest[16] = (unsigned char) ((sha_info->digest[4] >> 24) & 0xff); | 
 | 355 |     digest[17] = (unsigned char) ((sha_info->digest[4] >> 16) & 0xff); | 
 | 356 |     digest[18] = (unsigned char) ((sha_info->digest[4] >>  8) & 0xff); | 
 | 357 |     digest[19] = (unsigned char) ((sha_info->digest[4]      ) & 0xff); | 
 | 358 |     digest[20] = (unsigned char) ((sha_info->digest[5] >> 24) & 0xff); | 
 | 359 |     digest[21] = (unsigned char) ((sha_info->digest[5] >> 16) & 0xff); | 
 | 360 |     digest[22] = (unsigned char) ((sha_info->digest[5] >>  8) & 0xff); | 
 | 361 |     digest[23] = (unsigned char) ((sha_info->digest[5]      ) & 0xff); | 
 | 362 |     digest[24] = (unsigned char) ((sha_info->digest[6] >> 24) & 0xff); | 
 | 363 |     digest[25] = (unsigned char) ((sha_info->digest[6] >> 16) & 0xff); | 
 | 364 |     digest[26] = (unsigned char) ((sha_info->digest[6] >>  8) & 0xff); | 
 | 365 |     digest[27] = (unsigned char) ((sha_info->digest[6]      ) & 0xff); | 
 | 366 |     digest[28] = (unsigned char) ((sha_info->digest[7] >> 24) & 0xff); | 
 | 367 |     digest[29] = (unsigned char) ((sha_info->digest[7] >> 16) & 0xff); | 
 | 368 |     digest[30] = (unsigned char) ((sha_info->digest[7] >>  8) & 0xff); | 
 | 369 |     digest[31] = (unsigned char) ((sha_info->digest[7]      ) & 0xff); | 
 | 370 | } | 
 | 371 |  | 
 | 372 | /* | 
 | 373 |  * End of copied SHA code. | 
 | 374 |  * | 
 | 375 |  * ------------------------------------------------------------------------ | 
 | 376 |  */ | 
 | 377 |  | 
 | 378 | static PyTypeObject SHA224type; | 
 | 379 | static PyTypeObject SHA256type; | 
 | 380 |  | 
 | 381 |  | 
 | 382 | static SHAobject * | 
 | 383 | newSHA224object(void) | 
 | 384 | { | 
 | 385 |     return (SHAobject *)PyObject_New(SHAobject, &SHA224type); | 
 | 386 | } | 
 | 387 |  | 
 | 388 | static SHAobject * | 
 | 389 | newSHA256object(void) | 
 | 390 | { | 
 | 391 |     return (SHAobject *)PyObject_New(SHAobject, &SHA256type); | 
 | 392 | } | 
 | 393 |  | 
 | 394 | /* Internal methods for a hash object */ | 
 | 395 |  | 
 | 396 | static void | 
 | 397 | SHA_dealloc(PyObject *ptr) | 
 | 398 | { | 
 | 399 |     PyObject_Del(ptr); | 
 | 400 | } | 
 | 401 |  | 
 | 402 |  | 
 | 403 | /* External methods for a hash object */ | 
 | 404 |  | 
 | 405 | PyDoc_STRVAR(SHA256_copy__doc__, "Return a copy of the hash object."); | 
 | 406 |  | 
 | 407 | static PyObject * | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 408 | SHA256_copy(SHAobject *self, PyObject *unused) | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 409 | { | 
 | 410 |     SHAobject *newobj; | 
 | 411 |  | 
| Christian Heimes | 90aa764 | 2007-12-19 02:45:37 +0000 | [diff] [blame] | 412 |     if (Py_TYPE(self) == &SHA256type) { | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 413 |         if ( (newobj = newSHA256object())==NULL) | 
 | 414 |             return NULL; | 
 | 415 |     } else { | 
 | 416 |         if ( (newobj = newSHA224object())==NULL) | 
 | 417 |             return NULL; | 
 | 418 |     } | 
 | 419 |  | 
 | 420 |     SHAcopy(self, newobj); | 
 | 421 |     return (PyObject *)newobj; | 
 | 422 | } | 
 | 423 |  | 
 | 424 | PyDoc_STRVAR(SHA256_digest__doc__, | 
 | 425 | "Return the digest value as a string of binary data."); | 
 | 426 |  | 
 | 427 | static PyObject * | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 428 | SHA256_digest(SHAobject *self, PyObject *unused) | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 429 | { | 
 | 430 |     unsigned char digest[SHA_DIGESTSIZE]; | 
 | 431 |     SHAobject temp; | 
 | 432 |  | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 433 |     SHAcopy(self, &temp); | 
 | 434 |     sha_final(digest, &temp); | 
| Christian Heimes | 72b710a | 2008-05-26 13:28:38 +0000 | [diff] [blame] | 435 |     return PyBytes_FromStringAndSize((const char *)digest, self->digestsize); | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 436 | } | 
 | 437 |  | 
 | 438 | PyDoc_STRVAR(SHA256_hexdigest__doc__, | 
 | 439 | "Return the digest value as a string of hexadecimal digits."); | 
 | 440 |  | 
 | 441 | static PyObject * | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 442 | SHA256_hexdigest(SHAobject *self, PyObject *unused) | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 443 | { | 
 | 444 |     unsigned char digest[SHA_DIGESTSIZE]; | 
 | 445 |     SHAobject temp; | 
 | 446 |     PyObject *retval; | 
| Neal Norwitz | b4e2f76 | 2007-08-25 08:01:41 +0000 | [diff] [blame] | 447 |     Py_UNICODE *hex_digest; | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 448 |     int i, j; | 
 | 449 |  | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 450 |     /* Get the raw (binary) digest value */ | 
 | 451 |     SHAcopy(self, &temp); | 
 | 452 |     sha_final(digest, &temp); | 
 | 453 |  | 
 | 454 |     /* Create a new string */ | 
| Neal Norwitz | b4e2f76 | 2007-08-25 08:01:41 +0000 | [diff] [blame] | 455 |     retval = PyUnicode_FromStringAndSize(NULL, self->digestsize * 2); | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 456 |     if (!retval) | 
 | 457 | 	    return NULL; | 
| Neal Norwitz | b4e2f76 | 2007-08-25 08:01:41 +0000 | [diff] [blame] | 458 |     hex_digest = PyUnicode_AS_UNICODE(retval); | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 459 |     if (!hex_digest) { | 
 | 460 | 	    Py_DECREF(retval); | 
 | 461 | 	    return NULL; | 
 | 462 |     } | 
 | 463 |  | 
 | 464 |     /* Make hex version of the digest */ | 
 | 465 |     for(i=j=0; i<self->digestsize; i++) { | 
 | 466 |         char c; | 
 | 467 |         c = (digest[i] >> 4) & 0xf; | 
 | 468 | 	c = (c>9) ? c+'a'-10 : c + '0'; | 
 | 469 |         hex_digest[j++] = c; | 
 | 470 |         c = (digest[i] & 0xf); | 
 | 471 | 	c = (c>9) ? c+'a'-10 : c + '0'; | 
 | 472 |         hex_digest[j++] = c; | 
 | 473 |     } | 
 | 474 |     return retval; | 
 | 475 | } | 
 | 476 |  | 
 | 477 | PyDoc_STRVAR(SHA256_update__doc__, | 
 | 478 | "Update this hash object's state with the provided string."); | 
 | 479 |  | 
 | 480 | static PyObject * | 
 | 481 | SHA256_update(SHAobject *self, PyObject *args) | 
 | 482 | { | 
 | 483 |     unsigned char *cp; | 
 | 484 |     int len; | 
 | 485 |  | 
 | 486 |     if (!PyArg_ParseTuple(args, "s#:update", &cp, &len)) | 
 | 487 |         return NULL; | 
 | 488 |  | 
 | 489 |     sha_update(self, cp, len); | 
 | 490 |  | 
 | 491 |     Py_INCREF(Py_None); | 
 | 492 |     return Py_None; | 
 | 493 | } | 
 | 494 |  | 
 | 495 | static PyMethodDef SHA_methods[] = { | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 496 |     {"copy",	  (PyCFunction)SHA256_copy,      METH_NOARGS,  SHA256_copy__doc__}, | 
 | 497 |     {"digest",	  (PyCFunction)SHA256_digest,    METH_NOARGS,  SHA256_digest__doc__}, | 
 | 498 |     {"hexdigest", (PyCFunction)SHA256_hexdigest, METH_NOARGS,  SHA256_hexdigest__doc__}, | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 499 |     {"update",	  (PyCFunction)SHA256_update,    METH_VARARGS, SHA256_update__doc__}, | 
 | 500 |     {NULL,	  NULL}		/* sentinel */ | 
 | 501 | }; | 
 | 502 |  | 
 | 503 | static PyObject * | 
 | 504 | SHA256_get_block_size(PyObject *self, void *closure) | 
 | 505 | { | 
| Christian Heimes | 217cfd1 | 2007-12-02 14:31:20 +0000 | [diff] [blame] | 506 |     return PyLong_FromLong(SHA_BLOCKSIZE); | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 507 | } | 
 | 508 |  | 
 | 509 | static PyObject * | 
 | 510 | SHA256_get_name(PyObject *self, void *closure) | 
 | 511 | { | 
 | 512 |     if (((SHAobject *)self)->digestsize == 32) | 
| Guido van Rossum | 5ed033b | 2007-07-09 14:29:40 +0000 | [diff] [blame] | 513 |         return PyUnicode_FromStringAndSize("SHA256", 6); | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 514 |     else | 
| Guido van Rossum | 5ed033b | 2007-07-09 14:29:40 +0000 | [diff] [blame] | 515 |         return PyUnicode_FromStringAndSize("SHA224", 6); | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 516 | } | 
 | 517 |  | 
 | 518 | static PyGetSetDef SHA_getseters[] = { | 
 | 519 |     {"block_size", | 
 | 520 |      (getter)SHA256_get_block_size, NULL, | 
 | 521 |      NULL, | 
 | 522 |      NULL}, | 
 | 523 |     {"name", | 
 | 524 |      (getter)SHA256_get_name, NULL, | 
 | 525 |      NULL, | 
 | 526 |      NULL}, | 
 | 527 |     {NULL}  /* Sentinel */ | 
 | 528 | }; | 
 | 529 |  | 
 | 530 | static PyMemberDef SHA_members[] = { | 
 | 531 |     {"digest_size", T_INT, offsetof(SHAobject, digestsize), READONLY, NULL}, | 
 | 532 |     /* the old md5 and sha modules support 'digest_size' as in PEP 247. | 
 | 533 |      * the old sha module also supported 'digestsize'.  ugh. */ | 
 | 534 |     {"digestsize", T_INT, offsetof(SHAobject, digestsize), READONLY, NULL}, | 
 | 535 |     {NULL}  /* Sentinel */ | 
 | 536 | }; | 
 | 537 |  | 
 | 538 | static PyTypeObject SHA224type = { | 
| Martin v. Löwis | 9f2e346 | 2007-07-21 17:22:18 +0000 | [diff] [blame] | 539 |     PyVarObject_HEAD_INIT(NULL, 0) | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 540 |     "_sha256.sha224",	/*tp_name*/ | 
 | 541 |     sizeof(SHAobject),	/*tp_size*/ | 
 | 542 |     0,			/*tp_itemsize*/ | 
 | 543 |     /* methods */ | 
 | 544 |     SHA_dealloc,	/*tp_dealloc*/ | 
 | 545 |     0,			/*tp_print*/ | 
 | 546 |     0,          	/*tp_getattr*/ | 
 | 547 |     0,                  /*tp_setattr*/ | 
 | 548 |     0,                  /*tp_compare*/ | 
 | 549 |     0,                  /*tp_repr*/ | 
 | 550 |     0,                  /*tp_as_number*/ | 
 | 551 |     0,                  /*tp_as_sequence*/ | 
 | 552 |     0,                  /*tp_as_mapping*/ | 
 | 553 |     0,                  /*tp_hash*/ | 
 | 554 |     0,                  /*tp_call*/ | 
 | 555 |     0,                  /*tp_str*/ | 
 | 556 |     0,                  /*tp_getattro*/ | 
 | 557 |     0,                  /*tp_setattro*/ | 
 | 558 |     0,                  /*tp_as_buffer*/ | 
 | 559 |     Py_TPFLAGS_DEFAULT, /*tp_flags*/ | 
 | 560 |     0,                  /*tp_doc*/ | 
 | 561 |     0,                  /*tp_traverse*/ | 
 | 562 |     0,			/*tp_clear*/ | 
 | 563 |     0,			/*tp_richcompare*/ | 
 | 564 |     0,			/*tp_weaklistoffset*/ | 
 | 565 |     0,			/*tp_iter*/ | 
 | 566 |     0,			/*tp_iternext*/ | 
 | 567 |     SHA_methods,	/* tp_methods */ | 
 | 568 |     SHA_members,	/* tp_members */ | 
 | 569 |     SHA_getseters,      /* tp_getset */ | 
 | 570 | }; | 
 | 571 |  | 
 | 572 | static PyTypeObject SHA256type = { | 
| Martin v. Löwis | 9f2e346 | 2007-07-21 17:22:18 +0000 | [diff] [blame] | 573 |     PyVarObject_HEAD_INIT(NULL, 0) | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 574 |     "_sha256.sha256",	/*tp_name*/ | 
 | 575 |     sizeof(SHAobject),	/*tp_size*/ | 
 | 576 |     0,			/*tp_itemsize*/ | 
 | 577 |     /* methods */ | 
 | 578 |     SHA_dealloc,	/*tp_dealloc*/ | 
 | 579 |     0,			/*tp_print*/ | 
 | 580 |     0,          	/*tp_getattr*/ | 
 | 581 |     0,                  /*tp_setattr*/ | 
 | 582 |     0,                  /*tp_compare*/ | 
 | 583 |     0,                  /*tp_repr*/ | 
 | 584 |     0,                  /*tp_as_number*/ | 
 | 585 |     0,                  /*tp_as_sequence*/ | 
 | 586 |     0,                  /*tp_as_mapping*/ | 
 | 587 |     0,                  /*tp_hash*/ | 
 | 588 |     0,                  /*tp_call*/ | 
 | 589 |     0,                  /*tp_str*/ | 
 | 590 |     0,                  /*tp_getattro*/ | 
 | 591 |     0,                  /*tp_setattro*/ | 
 | 592 |     0,                  /*tp_as_buffer*/ | 
 | 593 |     Py_TPFLAGS_DEFAULT, /*tp_flags*/ | 
 | 594 |     0,                  /*tp_doc*/ | 
 | 595 |     0,                  /*tp_traverse*/ | 
 | 596 |     0,			/*tp_clear*/ | 
 | 597 |     0,			/*tp_richcompare*/ | 
 | 598 |     0,			/*tp_weaklistoffset*/ | 
 | 599 |     0,			/*tp_iter*/ | 
 | 600 |     0,			/*tp_iternext*/ | 
 | 601 |     SHA_methods,	/* tp_methods */ | 
 | 602 |     SHA_members,	/* tp_members */ | 
 | 603 |     SHA_getseters,      /* tp_getset */ | 
 | 604 | }; | 
 | 605 |  | 
 | 606 |  | 
 | 607 | /* The single module-level function: new() */ | 
 | 608 |  | 
 | 609 | PyDoc_STRVAR(SHA256_new__doc__, | 
 | 610 | "Return a new SHA-256 hash object; optionally initialized with a string."); | 
 | 611 |  | 
 | 612 | static PyObject * | 
 | 613 | SHA256_new(PyObject *self, PyObject *args, PyObject *kwdict) | 
 | 614 | { | 
| Martin v. Löwis | 15e6274 | 2006-02-27 16:46:16 +0000 | [diff] [blame] | 615 |     static char *kwlist[] = {"string", NULL}; | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 616 |     SHAobject *new; | 
 | 617 |     unsigned char *cp = NULL; | 
 | 618 |     int len; | 
 | 619 |  | 
 | 620 |     if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|s#:new", kwlist, | 
 | 621 |                                      &cp, &len)) { | 
 | 622 |         return NULL; | 
 | 623 |     } | 
 | 624 |  | 
 | 625 |     if ((new = newSHA256object()) == NULL) | 
 | 626 |         return NULL; | 
 | 627 |  | 
 | 628 |     sha_init(new); | 
 | 629 |  | 
 | 630 |     if (PyErr_Occurred()) { | 
 | 631 |         Py_DECREF(new); | 
 | 632 |         return NULL; | 
 | 633 |     } | 
 | 634 |     if (cp) | 
 | 635 |         sha_update(new, cp, len); | 
 | 636 |  | 
 | 637 |     return (PyObject *)new; | 
 | 638 | } | 
 | 639 |  | 
 | 640 | PyDoc_STRVAR(SHA224_new__doc__, | 
 | 641 | "Return a new SHA-224 hash object; optionally initialized with a string."); | 
 | 642 |  | 
 | 643 | static PyObject * | 
 | 644 | SHA224_new(PyObject *self, PyObject *args, PyObject *kwdict) | 
 | 645 | { | 
| Martin v. Löwis | 15e6274 | 2006-02-27 16:46:16 +0000 | [diff] [blame] | 646 |     static char *kwlist[] = {"string", NULL}; | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 647 |     SHAobject *new; | 
 | 648 |     unsigned char *cp = NULL; | 
 | 649 |     int len; | 
 | 650 |  | 
 | 651 |     if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|s#:new", kwlist, | 
 | 652 |                                      &cp, &len)) { | 
 | 653 |         return NULL; | 
 | 654 |     } | 
 | 655 |  | 
 | 656 |     if ((new = newSHA224object()) == NULL) | 
 | 657 |         return NULL; | 
 | 658 |  | 
 | 659 |     sha224_init(new); | 
 | 660 |  | 
 | 661 |     if (PyErr_Occurred()) { | 
 | 662 |         Py_DECREF(new); | 
 | 663 |         return NULL; | 
 | 664 |     } | 
 | 665 |     if (cp) | 
 | 666 |         sha_update(new, cp, len); | 
 | 667 |  | 
 | 668 |     return (PyObject *)new; | 
 | 669 | } | 
 | 670 |  | 
 | 671 |  | 
 | 672 | /* List of functions exported by this module */ | 
 | 673 |  | 
 | 674 | static struct PyMethodDef SHA_functions[] = { | 
 | 675 |     {"sha256", (PyCFunction)SHA256_new, METH_VARARGS|METH_KEYWORDS, SHA256_new__doc__}, | 
 | 676 |     {"sha224", (PyCFunction)SHA224_new, METH_VARARGS|METH_KEYWORDS, SHA224_new__doc__}, | 
 | 677 |     {NULL,	NULL}		 /* Sentinel */ | 
 | 678 | }; | 
 | 679 |  | 
 | 680 |  | 
 | 681 | /* Initialize this module. */ | 
 | 682 |  | 
 | 683 | #define insint(n,v) { PyModule_AddIntConstant(m,n,v); } | 
 | 684 |  | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 685 |  | 
| Martin v. Löwis | 1a21451 | 2008-06-11 05:26:20 +0000 | [diff] [blame] | 686 | static struct PyModuleDef _sha256module = { | 
 | 687 | 	PyModuleDef_HEAD_INIT, | 
 | 688 | 	"_sha256", | 
 | 689 | 	NULL, | 
 | 690 | 	-1, | 
 | 691 | 	SHA_functions, | 
 | 692 | 	NULL, | 
 | 693 | 	NULL, | 
 | 694 | 	NULL, | 
 | 695 | 	NULL | 
 | 696 | }; | 
 | 697 |  | 
 | 698 | PyMODINIT_FUNC | 
 | 699 | PyInit__sha256(void) | 
 | 700 | { | 
| Christian Heimes | 90aa764 | 2007-12-19 02:45:37 +0000 | [diff] [blame] | 701 |     Py_TYPE(&SHA224type) = &PyType_Type; | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 702 |     if (PyType_Ready(&SHA224type) < 0) | 
| Martin v. Löwis | 1a21451 | 2008-06-11 05:26:20 +0000 | [diff] [blame] | 703 |         return NULL; | 
| Christian Heimes | 90aa764 | 2007-12-19 02:45:37 +0000 | [diff] [blame] | 704 |     Py_TYPE(&SHA256type) = &PyType_Type; | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 705 |     if (PyType_Ready(&SHA256type) < 0) | 
| Martin v. Löwis | 1a21451 | 2008-06-11 05:26:20 +0000 | [diff] [blame] | 706 |         return NULL; | 
 | 707 |     return PyModule_Create(&_sha256module); | 
| Gregory P. Smith | f21a5f7 | 2005-08-21 18:45:59 +0000 | [diff] [blame] | 708 | } |