| 1 | // Licensed to the .NET Foundation under one or more agreements. | 
|---|
| 2 | // The .NET Foundation licenses this file to you under the MIT license. | 
|---|
| 3 | // See the LICENSE file in the project root for more information. | 
|---|
| 4 | // | 
|---|
| 5 | // | 
|---|
| 6 | // | 
|---|
| 7 | // =========================================================================== | 
|---|
| 8 | // File: sha1.cpp | 
|---|
| 9 | // | 
|---|
| 10 | // =========================================================================== | 
|---|
| 11 | /*++ | 
|---|
| 12 |  | 
|---|
| 13 | Abstract: | 
|---|
| 14 |  | 
|---|
| 15 | SHA-1 implementation | 
|---|
| 16 |  | 
|---|
| 17 | Revision History: | 
|---|
| 18 |  | 
|---|
| 19 | --*/ | 
|---|
| 20 |  | 
|---|
| 21 | /* | 
|---|
| 22 | File sha1.cpp    <STRIP>Version 03 August 2000.</STRIP> | 
|---|
| 23 |  | 
|---|
| 24 |  | 
|---|
| 25 | This implements the SHA-1 hash function. | 
|---|
| 26 | For algorithmic background see (for example) | 
|---|
| 27 |  | 
|---|
| 28 |  | 
|---|
| 29 | Alfred J. Menezes et al | 
|---|
| 30 | Handbook of Applied Cryptography | 
|---|
| 31 | The CRC Press Series on Discrete Mathematics | 
|---|
| 32 | and its Applications | 
|---|
| 33 | CRC Press LLC, 1997 | 
|---|
| 34 | ISBN 0-8495-8523-7 | 
|---|
| 35 | QA76.9A25M643 | 
|---|
| 36 |  | 
|---|
| 37 | Also see FIPS 180-1 - Secure Hash Standard, | 
|---|
| 38 | 1993 May 11 and 1995 April 17, by the U.S. | 
|---|
| 39 | National Institute of Standards and Technology (NIST). | 
|---|
| 40 |  | 
|---|
| 41 | */ | 
|---|
| 42 |  | 
|---|
| 43 | #include "common.h" | 
|---|
| 44 | #include "sha1.h" | 
|---|
| 45 | #include "contract.h" | 
|---|
| 46 |  | 
|---|
| 47 | typedef const DWORD DWORDC; | 
|---|
| 48 | #define ROTATE32L(x,n) _rotl(x,n) | 
|---|
| 49 | #define SHAVE32(x)     (DWORD)(x) | 
|---|
| 50 |  | 
|---|
| 51 | static void SHA1_block(SHA1_CTX *ctx) | 
|---|
| 52 | /* | 
|---|
| 53 | Update the SHA-1 hash from a fresh 64 bytes of data. | 
|---|
| 54 | */ | 
|---|
| 55 | { | 
|---|
| 56 | static DWORDC sha1_round1 = 0x5A827999u; | 
|---|
| 57 | static DWORDC sha1_round2 = 0x6ED9EBA1u; | 
|---|
| 58 | static DWORDC sha1_round3 = 0x8F1BBCDCu; | 
|---|
| 59 | static DWORDC sha1_round4 = 0xCA62C1D6u; | 
|---|
| 60 |  | 
|---|
| 61 | DWORD a = ctx->partial_hash[0], b = ctx->partial_hash[1]; | 
|---|
| 62 | DWORD c = ctx->partial_hash[2], d = ctx->partial_hash[3]; | 
|---|
| 63 | DWORD e = ctx->partial_hash[4]; | 
|---|
| 64 | DWORD  msg80[80]; | 
|---|
| 65 | int i; | 
|---|
| 66 | BOOL OK = TRUE; | 
|---|
| 67 |  | 
|---|
| 68 | // OACR note: | 
|---|
| 69 | // Loop conditions are using (i <= limit - increment) instead of (i < limit) to satisfy OACR. When the increment is greater | 
|---|
| 70 | // than 1, OACR incorrectly thinks that the max value of 'i' is (limit - 1). | 
|---|
| 71 |  | 
|---|
| 72 | for (i = 0; i < 16; i++) {   // Copy to local array, zero original | 
|---|
| 73 | // Extend length to 80 | 
|---|
| 74 | DWORDC datval = ctx->awaiting_data[i]; | 
|---|
| 75 | ctx->awaiting_data[i] = 0; | 
|---|
| 76 | msg80[i] = datval; | 
|---|
| 77 | } | 
|---|
| 78 |  | 
|---|
| 79 | for (i = 16; i <= 80 - 2; i += 2) { | 
|---|
| 80 | DWORDC temp1 =    msg80[i-3] ^ msg80[i-8] | 
|---|
| 81 | ^ msg80[i-14] ^ msg80[i-16]; | 
|---|
| 82 | DWORDC temp2 =    msg80[i-2] ^ msg80[i-7] | 
|---|
| 83 | ^ msg80[i-13] ^ msg80[i-15]; | 
|---|
| 84 | msg80[i  ] = ROTATE32L(temp1, 1); | 
|---|
| 85 | msg80[i+1] = ROTATE32L(temp2, 1); | 
|---|
| 86 | } | 
|---|
| 87 |  | 
|---|
| 88 | #define ROUND1(B, C, D) ((D ^ (B & (C ^ D))) + sha1_round1) | 
|---|
| 89 | //  Equivalent to (B & C) | (~B & D). | 
|---|
| 90 | //  (check cases B = 0 and B = 1) | 
|---|
| 91 | #define ROUND2(B, C, D) ((B ^ C ^ D) + sha1_round2) | 
|---|
| 92 |  | 
|---|
| 93 | #define ROUND3(B, C, D) ((C & (B | D) | (B & D)) + sha1_round3) | 
|---|
| 94 |  | 
|---|
| 95 | #define ROUND4(B, C, D) ((B ^ C ^ D) + sha1_round4) | 
|---|
| 96 |  | 
|---|
| 97 | // Round 1 | 
|---|
| 98 | for (i = 0; i <= 20 - 5; i += 5) { | 
|---|
| 99 | e += ROTATE32L(a, 5) + ROUND1(b, c, d) + msg80[i]; | 
|---|
| 100 | b = ROTATE32L(b, 30); | 
|---|
| 101 |  | 
|---|
| 102 | d += ROTATE32L(e, 5) + ROUND1(a, b, c) + msg80[i+1]; | 
|---|
| 103 | a = ROTATE32L(a, 30); | 
|---|
| 104 |  | 
|---|
| 105 | c += ROTATE32L(d, 5) + ROUND1(e, a, b) + msg80[i+2]; | 
|---|
| 106 | e = ROTATE32L(e, 30); | 
|---|
| 107 |  | 
|---|
| 108 | b += ROTATE32L(c, 5) + ROUND1(d, e, a) + msg80[i+3]; | 
|---|
| 109 | d = ROTATE32L(d, 30); | 
|---|
| 110 |  | 
|---|
| 111 | a += ROTATE32L(b, 5) + ROUND1(c, d, e) + msg80[i+4]; | 
|---|
| 112 | c = ROTATE32L(c, 30); | 
|---|
| 113 | #if 0 | 
|---|
| 114 | printf( "i = %ld %08lx %08lx %08lx %08lx %08lx\n", | 
|---|
| 115 | i, a, b, c, d, e); | 
|---|
| 116 | #endif | 
|---|
| 117 | } // for i | 
|---|
| 118 |  | 
|---|
| 119 | // Round 2 | 
|---|
| 120 | for (i = 20; i <= 40 - 5; i += 5) { | 
|---|
| 121 | e += ROTATE32L(a, 5) + ROUND2(b, c, d) + msg80[i]; | 
|---|
| 122 | b = ROTATE32L(b, 30); | 
|---|
| 123 |  | 
|---|
| 124 | d += ROTATE32L(e, 5) + ROUND2(a, b, c) + msg80[i+1]; | 
|---|
| 125 | a = ROTATE32L(a, 30); | 
|---|
| 126 |  | 
|---|
| 127 | c += ROTATE32L(d, 5) + ROUND2(e, a, b) + msg80[i+2]; | 
|---|
| 128 | e = ROTATE32L(e, 30); | 
|---|
| 129 |  | 
|---|
| 130 | b += ROTATE32L(c, 5) + ROUND2(d, e, a) + msg80[i+3]; | 
|---|
| 131 | d = ROTATE32L(d, 30); | 
|---|
| 132 |  | 
|---|
| 133 | a += ROTATE32L(b, 5) + ROUND2(c, d, e) + msg80[i+4]; | 
|---|
| 134 | c = ROTATE32L(c, 30); | 
|---|
| 135 | } // for i | 
|---|
| 136 |  | 
|---|
| 137 | // Round 3 | 
|---|
| 138 | for (i = 40; i <= 60 - 5; i += 5) { | 
|---|
| 139 | e += ROTATE32L(a, 5) + ROUND3(b, c, d) + msg80[i]; | 
|---|
| 140 | b = ROTATE32L(b, 30); | 
|---|
| 141 |  | 
|---|
| 142 | d += ROTATE32L(e, 5) + ROUND3(a, b, c) + msg80[i+1]; | 
|---|
| 143 | a = ROTATE32L(a, 30); | 
|---|
| 144 |  | 
|---|
| 145 | c += ROTATE32L(d, 5) + ROUND3(e, a, b) + msg80[i+2]; | 
|---|
| 146 | e = ROTATE32L(e, 30); | 
|---|
| 147 |  | 
|---|
| 148 | b += ROTATE32L(c, 5) + ROUND3(d, e, a) + msg80[i+3]; | 
|---|
| 149 | d = ROTATE32L(d, 30); | 
|---|
| 150 |  | 
|---|
| 151 | a += ROTATE32L(b, 5) + ROUND3(c, d, e) + msg80[i+4]; | 
|---|
| 152 | c = ROTATE32L(c, 30); | 
|---|
| 153 | } // for i | 
|---|
| 154 |  | 
|---|
| 155 | // Round 4 | 
|---|
| 156 | for (i = 60; i <= 80 - 5; i += 5) { | 
|---|
| 157 | e += ROTATE32L(a, 5) + ROUND4(b, c, d) + msg80[i]; | 
|---|
| 158 | b = ROTATE32L(b, 30); | 
|---|
| 159 |  | 
|---|
| 160 | d += ROTATE32L(e, 5) + ROUND4(a, b, c) + msg80[i+1]; | 
|---|
| 161 | a = ROTATE32L(a, 30); | 
|---|
| 162 |  | 
|---|
| 163 | c += ROTATE32L(d, 5) + ROUND4(e, a, b) + msg80[i+2]; | 
|---|
| 164 | e = ROTATE32L(e, 30); | 
|---|
| 165 |  | 
|---|
| 166 | b += ROTATE32L(c, 5) + ROUND4(d, e, a) + msg80[i+3]; | 
|---|
| 167 | d = ROTATE32L(d, 30); | 
|---|
| 168 |  | 
|---|
| 169 | a += ROTATE32L(b, 5) + ROUND4(c, d, e) + msg80[i+4]; | 
|---|
| 170 | c = ROTATE32L(c, 30); | 
|---|
| 171 | } // for i | 
|---|
| 172 |  | 
|---|
| 173 | #undef ROUND1 | 
|---|
| 174 | #undef ROUND2 | 
|---|
| 175 | #undef ROUND3 | 
|---|
| 176 | #undef ROUND4 | 
|---|
| 177 |  | 
|---|
| 178 | ctx->partial_hash[0] += a; | 
|---|
| 179 | ctx->partial_hash[1] += b; | 
|---|
| 180 | ctx->partial_hash[2] += c; | 
|---|
| 181 | ctx->partial_hash[3] += d; | 
|---|
| 182 | ctx->partial_hash[4] += e; | 
|---|
| 183 | #if 0 | 
|---|
| 184 | for (i = 0; i < 16; i++) { | 
|---|
| 185 | printf( "%8lx ", msg16[i]); | 
|---|
| 186 | if ((i & 7) == 7) printf( "\n"); | 
|---|
| 187 | } | 
|---|
| 188 | printf( "a, b, c, d, e = %08lx %08lx %08lx %08lx %08lx\n", | 
|---|
| 189 | a, b, c, d, e); | 
|---|
| 190 | printf( "Partial hash = %08lx %08lx %08lx %08lx %08lx\n", | 
|---|
| 191 | (long)ctx->partial_hash[0], (long)ctx->partial_hash[1], | 
|---|
| 192 | (long)ctx->partial_hash[2], (long)ctx->partial_hash[3], | 
|---|
| 193 | (long)ctx->partial_hash[4]); | 
|---|
| 194 | #endif | 
|---|
| 195 | } // end SHA1_block | 
|---|
| 196 |  | 
|---|
| 197 |  | 
|---|
| 198 | void SHA1Hash::SHA1Init(SHA1_CTX *ctx) | 
|---|
| 199 | { | 
|---|
| 200 | CONTRACTL { | 
|---|
| 201 | NOTHROW; | 
|---|
| 202 | GC_NOTRIGGER; | 
|---|
| 203 | MODE_ANY; | 
|---|
| 204 | SO_TOLERANT; | 
|---|
| 205 | } CONTRACTL_END; | 
|---|
| 206 |  | 
|---|
| 207 | ctx->nbit_total[0] = ctx->nbit_total[1] = 0; | 
|---|
| 208 |  | 
|---|
| 209 | for (DWORD i = 0; i != 16; i++) { | 
|---|
| 210 | ctx->awaiting_data[i] = 0; | 
|---|
| 211 | } | 
|---|
| 212 |  | 
|---|
| 213 | /* | 
|---|
| 214 | Initialize hash variables. | 
|---|
| 215 |  | 
|---|
| 216 | */ | 
|---|
| 217 |  | 
|---|
| 218 | ctx->partial_hash[0] = 0x67452301u; | 
|---|
| 219 | ctx->partial_hash[1] = 0xefcdab89u; | 
|---|
| 220 | ctx->partial_hash[2] = ~ctx->partial_hash[0]; | 
|---|
| 221 | ctx->partial_hash[3] = ~ctx->partial_hash[1]; | 
|---|
| 222 | ctx->partial_hash[4] = 0xc3d2e1f0u; | 
|---|
| 223 |  | 
|---|
| 224 | } | 
|---|
| 225 |  | 
|---|
| 226 | void SHA1Hash::SHA1Update( | 
|---|
| 227 | SHA1_CTX *  ctx,        // IN/OUT | 
|---|
| 228 | const BYTE *    msg,    // IN | 
|---|
| 229 | DWORD           nbyte)  // IN | 
|---|
| 230 | /* | 
|---|
| 231 | Append data to a partially hashed SHA-1 message. | 
|---|
| 232 | */ | 
|---|
| 233 | { | 
|---|
| 234 | CONTRACTL { | 
|---|
| 235 | NOTHROW; | 
|---|
| 236 | GC_NOTRIGGER; | 
|---|
| 237 | MODE_ANY; | 
|---|
| 238 | SO_TOLERANT; | 
|---|
| 239 | } CONTRACTL_END; | 
|---|
| 240 |  | 
|---|
| 241 | const BYTE *fresh_data = msg; | 
|---|
| 242 | DWORD nbyte_left = nbyte; | 
|---|
| 243 | DWORD nbit_occupied = ctx->nbit_total[0] & 511; | 
|---|
| 244 | DWORD *awaiting_data; | 
|---|
| 245 | DWORDC nbitnew_low = SHAVE32(8*nbyte); | 
|---|
| 246 |  | 
|---|
| 247 |  | 
|---|
| 248 | _ASSERTE((nbit_occupied & 7) == 0);   // Partial bytes not implemented | 
|---|
| 249 |  | 
|---|
| 250 | ctx->nbit_total[0] += nbitnew_low; | 
|---|
| 251 | ctx->nbit_total[1] += (nbyte >> 29) | 
|---|
| 252 | + (SHAVE32(ctx->nbit_total[0]) < nbitnew_low); | 
|---|
| 253 |  | 
|---|
| 254 | /* Advance to word boundary in waiting_data */ | 
|---|
| 255 |  | 
|---|
| 256 | if ((nbit_occupied & 31) != 0) { | 
|---|
| 257 | awaiting_data = ctx->awaiting_data + nbit_occupied/32; | 
|---|
| 258 |  | 
|---|
| 259 | while ((nbit_occupied & 31) != 0 && nbyte_left != 0) { | 
|---|
| 260 | nbit_occupied += 8; | 
|---|
| 261 | *awaiting_data |= (DWORD)*fresh_data++ | 
|---|
| 262 | << ((-(int)nbit_occupied) & 31); | 
|---|
| 263 | nbyte_left--;            // Start at most significant byte | 
|---|
| 264 | } | 
|---|
| 265 | } // if nbit_occupied | 
|---|
| 266 |  | 
|---|
| 267 | /* Transfer 4 bytes at a time */ | 
|---|
| 268 |  | 
|---|
| 269 | do { | 
|---|
| 270 | DWORDC nword_occupied = nbit_occupied/32; | 
|---|
| 271 | DWORD nwcopy = min(nbyte_left/4, 16 - nword_occupied); | 
|---|
| 272 | _ASSERTE (nbit_occupied <= 512); | 
|---|
| 273 | _ASSERTE ((nbit_occupied & 31) == 0 || nbyte_left == 0); | 
|---|
| 274 | awaiting_data = ctx->awaiting_data + nword_occupied; | 
|---|
| 275 | nbyte_left -= 4*nwcopy; | 
|---|
| 276 | nbit_occupied += 32*nwcopy; | 
|---|
| 277 |  | 
|---|
| 278 | while (nwcopy != 0) { | 
|---|
| 279 | DWORDC byte0 = (DWORD)fresh_data[0]; | 
|---|
| 280 | DWORDC byte1 = (DWORD)fresh_data[1]; | 
|---|
| 281 | DWORDC byte2 = (DWORD)fresh_data[2]; | 
|---|
| 282 | DWORDC byte3 = (DWORD)fresh_data[3]; | 
|---|
| 283 | *awaiting_data++ = byte3 | (byte2 << 8) | 
|---|
| 284 | | (byte1 << 16) | (byte0 << 24); | 
|---|
| 285 | /* Big endian */ | 
|---|
| 286 | fresh_data += 4; | 
|---|
| 287 | nwcopy--; | 
|---|
| 288 | } | 
|---|
| 289 |  | 
|---|
| 290 | if (nbit_occupied == 512) { | 
|---|
| 291 | SHA1_block(ctx); | 
|---|
| 292 | nbit_occupied = 0; | 
|---|
| 293 | awaiting_data -= 16; | 
|---|
| 294 | _ASSERTE(awaiting_data == ctx->awaiting_data); | 
|---|
| 295 | } | 
|---|
| 296 | } while (nbyte_left >= 4); | 
|---|
| 297 |  | 
|---|
| 298 | _ASSERTE (ctx->awaiting_data + nbit_occupied/32 | 
|---|
| 299 | == awaiting_data); | 
|---|
| 300 |  | 
|---|
| 301 | while (nbyte_left != 0) { | 
|---|
| 302 | DWORDC new_byte = (DWORD)*fresh_data++; | 
|---|
| 303 |  | 
|---|
| 304 | _ASSERTE((nbit_occupied & 31) <= 16); | 
|---|
| 305 | nbit_occupied += 8; | 
|---|
| 306 | *awaiting_data |= new_byte << ((-(int)nbit_occupied) & 31); | 
|---|
| 307 | nbyte_left--; | 
|---|
| 308 | } | 
|---|
| 309 |  | 
|---|
| 310 | _ASSERTE (nbit_occupied == (ctx->nbit_total[0] & 511)); | 
|---|
| 311 | } // end SHA1Update | 
|---|
| 312 |  | 
|---|
| 313 |  | 
|---|
| 314 |  | 
|---|
| 315 | void SHA1Hash::SHA1Final( | 
|---|
| 316 | SHA1_CTX *  ctx,            // IN/OUT | 
|---|
| 317 | BYTE *          digest)     // OUT | 
|---|
| 318 | /* | 
|---|
| 319 | Finish a SHA-1 hash. | 
|---|
| 320 | */ | 
|---|
| 321 | { | 
|---|
| 322 | CONTRACTL { | 
|---|
| 323 | NOTHROW; | 
|---|
| 324 | GC_NOTRIGGER; | 
|---|
| 325 | MODE_ANY; | 
|---|
| 326 | SO_TOLERANT; | 
|---|
| 327 | } CONTRACTL_END; | 
|---|
| 328 |  | 
|---|
| 329 | DWORDC nbit0 = ctx->nbit_total[0]; | 
|---|
| 330 | DWORDC nbit1 = ctx->nbit_total[1]; | 
|---|
| 331 | DWORD  nbit_occupied = nbit0 & 511; | 
|---|
| 332 | DWORD i; | 
|---|
| 333 |  | 
|---|
| 334 | _ASSERTE((nbit_occupied & 7) == 0); | 
|---|
| 335 |  | 
|---|
| 336 | ctx->awaiting_data[nbit_occupied/32] | 
|---|
| 337 | |= (DWORD)0x80 << ((-8-nbit_occupied) & 31); | 
|---|
| 338 | // Append a 1 bit | 
|---|
| 339 | nbit_occupied += 8; | 
|---|
| 340 |  | 
|---|
| 341 |  | 
|---|
| 342 | // Append zero bits until length (in bits) is 448 mod 512. | 
|---|
| 343 | // Then append the length, in bits. | 
|---|
| 344 | // Here we assume the buffer was zeroed earlier. | 
|---|
| 345 |  | 
|---|
| 346 | if (nbit_occupied > 448) {   // If fewer than 64 bits left | 
|---|
| 347 | SHA1_block(ctx); | 
|---|
| 348 | nbit_occupied = 0; | 
|---|
| 349 | } | 
|---|
| 350 | ctx->awaiting_data[14] = nbit1; | 
|---|
| 351 | ctx->awaiting_data[15] = nbit0; | 
|---|
| 352 | SHA1_block(ctx); | 
|---|
| 353 |  | 
|---|
| 354 | /* Copy final digest to user-supplied byte array */ | 
|---|
| 355 |  | 
|---|
| 356 | for (i = 0; i != 5; i++) { | 
|---|
| 357 | DWORDC dwi = ctx->partial_hash[i]; | 
|---|
| 358 | digest[4*i + 0] = (BYTE)((dwi >> 24) & 255); | 
|---|
| 359 | digest[4*i + 1] = (BYTE)((dwi >> 16) & 255); | 
|---|
| 360 | digest[4*i + 2] = (BYTE)((dwi >>  8) & 255); | 
|---|
| 361 | digest[4*i + 3] = (BYTE)(dwi         & 255);  // Big-endian | 
|---|
| 362 | } | 
|---|
| 363 | } // end SHA1Final | 
|---|
| 364 |  | 
|---|
| 365 | SHA1Hash::SHA1Hash() | 
|---|
| 366 | { | 
|---|
| 367 | CONTRACTL { | 
|---|
| 368 | NOTHROW; | 
|---|
| 369 | GC_NOTRIGGER; | 
|---|
| 370 | MODE_ANY; | 
|---|
| 371 | SO_TOLERANT; | 
|---|
| 372 | } CONTRACTL_END; | 
|---|
| 373 |  | 
|---|
| 374 | m_fFinalized = FALSE; | 
|---|
| 375 | SHA1Init(&m_Context); | 
|---|
| 376 | } | 
|---|
| 377 |  | 
|---|
| 378 | void SHA1Hash::AddData(BYTE *pbData, DWORD cbData) | 
|---|
| 379 | { | 
|---|
| 380 | CONTRACTL { | 
|---|
| 381 | NOTHROW; | 
|---|
| 382 | GC_NOTRIGGER; | 
|---|
| 383 | MODE_ANY; | 
|---|
| 384 | SO_TOLERANT; | 
|---|
| 385 | } CONTRACTL_END; | 
|---|
| 386 |  | 
|---|
| 387 | if (m_fFinalized) | 
|---|
| 388 | return; | 
|---|
| 389 |  | 
|---|
| 390 | SHA1Update(&m_Context, pbData, cbData); | 
|---|
| 391 | } | 
|---|
| 392 |  | 
|---|
| 393 | // Retrieve a pointer to the final hash. | 
|---|
| 394 | BYTE *SHA1Hash::GetHash() | 
|---|
| 395 | { | 
|---|
| 396 | CONTRACTL { | 
|---|
| 397 | NOTHROW; | 
|---|
| 398 | GC_NOTRIGGER; | 
|---|
| 399 | MODE_ANY; | 
|---|
| 400 | SO_TOLERANT; | 
|---|
| 401 | } CONTRACTL_END; | 
|---|
| 402 |  | 
|---|
| 403 | if (m_fFinalized) | 
|---|
| 404 | return m_Value; | 
|---|
| 405 |  | 
|---|
| 406 | SHA1Final(&m_Context, m_Value); | 
|---|
| 407 |  | 
|---|
| 408 | m_fFinalized = TRUE; | 
|---|
| 409 |  | 
|---|
| 410 | return m_Value; | 
|---|
| 411 | } | 
|---|
| 412 |  | 
|---|
| 413 |  | 
|---|