rufus/src/checksum.c

1031 lines
31 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Rufus: The Reliable USB Formatting Utility
* Message-Digest algorithms (md5sum, sha1sum, sha256sum)
* Copyright © 1998-2001 Free Software Foundation, Inc.
* Copyright © 2004 g10 Code GmbH
* Copyright © 2002-2015 Wei Dai & Igor Pavlov
* Copyright © 2015-2020 Pete Batard <pete@akeo.ie>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* SHA-1 code taken from GnuPG, as per copyrights above.
*
* SHA-256 taken from 7-zip's Sha256.c, itself based on Crypto++ - Public Domain
*
* MD5 code from various public domain sources sharing the following
* copyright declaration:
*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* written by Colin Plumb in 1993, no copyright is claimed.
* This code is in the public domain; do with it what you wish.
*
* Equivalent code is available from RSA Data Security, Inc.
* This code has been tested against that, and is equivalent,
* except that you don't need to include two pages of legalese
* with every copy.
*
* To compute the message digest of a chunk of bytes, declare an
* MD5Context structure, pass it to MD5Init, call MD5Update as
* needed on buffers full of bytes, and then call MD5Final, which
* will fill a supplied 16-byte array with the digest.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <errno.h>
#include <windowsx.h>
#include "db.h"
#include "rufus.h"
#include "missing.h"
#include "resource.h"
#include "msapi_utf8.h"
#include "localization.h"
#undef BIG_ENDIAN_HOST
#define BUFFER_SIZE (64*KB)
#define WAIT_TIME 5000
/* Blocksize for each algorithm - Must be a power of 2 */
#define MD5_BLOCKSIZE 64
#define SHA1_BLOCKSIZE 64
#define SHA256_BLOCKSIZE 64
/* Globals */
char sum_str[CHECKSUM_MAX][65];
uint32_t bufnum, sum_count[CHECKSUM_MAX] = { 16, 20, 32 };
HANDLE data_ready[CHECKSUM_MAX] = { 0 }, thread_ready[CHECKSUM_MAX] = { 0 };
DWORD read_size[2];
uint8_t ALIGNED(64) buffer[2][BUFFER_SIZE];
/*
* Rotate 32 bit integers by n bytes.
* Don't bother trying to hand-optimize those, as the
* compiler usually does a pretty good job at that.
*/
#define ROL32(a,b) (((a) << (b)) | ((a) >> (32-(b))))
#define ROR32(a,b) (((a) >> (b)) | ((a) << (32-(b))))
/*
* SHA-256 macros (use Wikipedia SHA-2 names for clarity)
*/
#define Ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z))))
#define Ma(x,y,z) (((x) & (y)) | ((z) & ((x) | (y))))
/* SHA-256 constants */
static const uint32_t K256[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
/*
* For convenience, we use a common context for all the checksum algorithms,
* which means some elements may be unused...
*/
typedef struct ALIGNED(64) {
uint8_t buf[64];
uint32_t state[8];
uint64_t bytecount;
} SUM_CONTEXT;
static void md5_init(SUM_CONTEXT *ctx)
{
memset(ctx, 0, sizeof(*ctx));
ctx->state[0] = 0x67452301;
ctx->state[1] = 0xefcdab89;
ctx->state[2] = 0x98badcfe;
ctx->state[3] = 0x10325476;
}
static void sha1_init(SUM_CONTEXT *ctx)
{
memset(ctx, 0, sizeof(*ctx));
ctx->state[0] = 0x67452301;
ctx->state[1] = 0xefcdab89;
ctx->state[2] = 0x98badcfe;
ctx->state[3] = 0x10325476;
ctx->state[4] = 0xc3d2e1f0;
}
static void sha256_init(SUM_CONTEXT *ctx)
{
memset(ctx, 0, sizeof(*ctx));
ctx->state[0] = 0x6a09e667;
ctx->state[1] = 0xbb67ae85;
ctx->state[2] = 0x3c6ef372;
ctx->state[3] = 0xa54ff53a;
ctx->state[4] = 0x510e527f;
ctx->state[5] = 0x9b05688c;
ctx->state[6] = 0x1f83d9ab;
ctx->state[7] = 0x5be0cd19;
}
/* Transform the message X which consists of 16 32-bit-words (SHA-1) */
static void sha1_transform(SUM_CONTEXT *ctx, const uint8_t *data)
{
uint32_t a, b, c, d, e, tm, x[16];
/* get values from the chaining vars */
a = ctx->state[0];
b = ctx->state[1];
c = ctx->state[2];
d = ctx->state[3];
e = ctx->state[4];
#ifdef BIG_ENDIAN_HOST
memcpy(x, data, sizeof(x));
#else
{
unsigned k;
for (k = 0; k < 16; k += 4) {
const uint8_t *p2 = data + k * 4;
x[k] = read_swap32(p2);
x[k + 1] = read_swap32(p2 + 4);
x[k + 2] = read_swap32(p2 + 8);
x[k + 3] = read_swap32(p2 + 12);
}
}
#endif
#define K1 0x5a827999L
#define K2 0x6ed9eba1L
#define K3 0x8f1bbcdcL
#define K4 0xca62c1d6L
#define F1(x,y,z) ( z ^ ( x & ( y ^ z ) ) )
#define F2(x,y,z) ( x ^ y ^ z )
#define F3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) )
#define F4(x,y,z) ( x ^ y ^ z )
#define M(i) ( tm = x[i&0x0f] ^ x[(i-14)&0x0f] ^ x[(i-8)&0x0f] ^ x[(i-3)&0x0f], (x[i&0x0f] = ROL32(tm,1)) )
#define SHA1STEP(a, b, c, d, e, f, k, m) do { e += ROL32(a, 5) + f(b, c, d) + k + m; \
b = ROL32(b, 30); } while(0)
SHA1STEP(a, b, c, d, e, F1, K1, x[0]);
SHA1STEP(e, a, b, c, d, F1, K1, x[1]);
SHA1STEP(d, e, a, b, c, F1, K1, x[2]);
SHA1STEP(c, d, e, a, b, F1, K1, x[3]);
SHA1STEP(b, c, d, e, a, F1, K1, x[4]);
SHA1STEP(a, b, c, d, e, F1, K1, x[5]);
SHA1STEP(e, a, b, c, d, F1, K1, x[6]);
SHA1STEP(d, e, a, b, c, F1, K1, x[7]);
SHA1STEP(c, d, e, a, b, F1, K1, x[8]);
SHA1STEP(b, c, d, e, a, F1, K1, x[9]);
SHA1STEP(a, b, c, d, e, F1, K1, x[10]);
SHA1STEP(e, a, b, c, d, F1, K1, x[11]);
SHA1STEP(d, e, a, b, c, F1, K1, x[12]);
SHA1STEP(c, d, e, a, b, F1, K1, x[13]);
SHA1STEP(b, c, d, e, a, F1, K1, x[14]);
SHA1STEP(a, b, c, d, e, F1, K1, x[15]);
SHA1STEP(e, a, b, c, d, F1, K1, M(16));
SHA1STEP(d, e, a, b, c, F1, K1, M(17));
SHA1STEP(c, d, e, a, b, F1, K1, M(18));
SHA1STEP(b, c, d, e, a, F1, K1, M(19));
SHA1STEP(a, b, c, d, e, F2, K2, M(20));
SHA1STEP(e, a, b, c, d, F2, K2, M(21));
SHA1STEP(d, e, a, b, c, F2, K2, M(22));
SHA1STEP(c, d, e, a, b, F2, K2, M(23));
SHA1STEP(b, c, d, e, a, F2, K2, M(24));
SHA1STEP(a, b, c, d, e, F2, K2, M(25));
SHA1STEP(e, a, b, c, d, F2, K2, M(26));
SHA1STEP(d, e, a, b, c, F2, K2, M(27));
SHA1STEP(c, d, e, a, b, F2, K2, M(28));
SHA1STEP(b, c, d, e, a, F2, K2, M(29));
SHA1STEP(a, b, c, d, e, F2, K2, M(30));
SHA1STEP(e, a, b, c, d, F2, K2, M(31));
SHA1STEP(d, e, a, b, c, F2, K2, M(32));
SHA1STEP(c, d, e, a, b, F2, K2, M(33));
SHA1STEP(b, c, d, e, a, F2, K2, M(34));
SHA1STEP(a, b, c, d, e, F2, K2, M(35));
SHA1STEP(e, a, b, c, d, F2, K2, M(36));
SHA1STEP(d, e, a, b, c, F2, K2, M(37));
SHA1STEP(c, d, e, a, b, F2, K2, M(38));
SHA1STEP(b, c, d, e, a, F2, K2, M(39));
SHA1STEP(a, b, c, d, e, F3, K3, M(40));
SHA1STEP(e, a, b, c, d, F3, K3, M(41));
SHA1STEP(d, e, a, b, c, F3, K3, M(42));
SHA1STEP(c, d, e, a, b, F3, K3, M(43));
SHA1STEP(b, c, d, e, a, F3, K3, M(44));
SHA1STEP(a, b, c, d, e, F3, K3, M(45));
SHA1STEP(e, a, b, c, d, F3, K3, M(46));
SHA1STEP(d, e, a, b, c, F3, K3, M(47));
SHA1STEP(c, d, e, a, b, F3, K3, M(48));
SHA1STEP(b, c, d, e, a, F3, K3, M(49));
SHA1STEP(a, b, c, d, e, F3, K3, M(50));
SHA1STEP(e, a, b, c, d, F3, K3, M(51));
SHA1STEP(d, e, a, b, c, F3, K3, M(52));
SHA1STEP(c, d, e, a, b, F3, K3, M(53));
SHA1STEP(b, c, d, e, a, F3, K3, M(54));
SHA1STEP(a, b, c, d, e, F3, K3, M(55));
SHA1STEP(e, a, b, c, d, F3, K3, M(56));
SHA1STEP(d, e, a, b, c, F3, K3, M(57));
SHA1STEP(c, d, e, a, b, F3, K3, M(58));
SHA1STEP(b, c, d, e, a, F3, K3, M(59));
SHA1STEP(a, b, c, d, e, F4, K4, M(60));
SHA1STEP(e, a, b, c, d, F4, K4, M(61));
SHA1STEP(d, e, a, b, c, F4, K4, M(62));
SHA1STEP(c, d, e, a, b, F4, K4, M(63));
SHA1STEP(b, c, d, e, a, F4, K4, M(64));
SHA1STEP(a, b, c, d, e, F4, K4, M(65));
SHA1STEP(e, a, b, c, d, F4, K4, M(66));
SHA1STEP(d, e, a, b, c, F4, K4, M(67));
SHA1STEP(c, d, e, a, b, F4, K4, M(68));
SHA1STEP(b, c, d, e, a, F4, K4, M(69));
SHA1STEP(a, b, c, d, e, F4, K4, M(70));
SHA1STEP(e, a, b, c, d, F4, K4, M(71));
SHA1STEP(d, e, a, b, c, F4, K4, M(72));
SHA1STEP(c, d, e, a, b, F4, K4, M(73));
SHA1STEP(b, c, d, e, a, F4, K4, M(74));
SHA1STEP(a, b, c, d, e, F4, K4, M(75));
SHA1STEP(e, a, b, c, d, F4, K4, M(76));
SHA1STEP(d, e, a, b, c, F4, K4, M(77));
SHA1STEP(c, d, e, a, b, F4, K4, M(78));
SHA1STEP(b, c, d, e, a, F4, K4, M(79));
#undef F1
#undef F2
#undef F3
#undef F4
/* Update chaining vars */
ctx->state[0] += a;
ctx->state[1] += b;
ctx->state[2] += c;
ctx->state[3] += d;
ctx->state[4] += e;
}
/* Transform the message X which consists of 16 32-bit-words (SHA-256) */
static __inline void sha256_transform(SUM_CONTEXT *ctx, const uint8_t *data)
{
uint32_t a, b, c, d, e, f, g, h, j, x[16];
a = ctx->state[0];
b = ctx->state[1];
c = ctx->state[2];
d = ctx->state[3];
e = ctx->state[4];
f = ctx->state[5];
g = ctx->state[6];
h = ctx->state[7];
// Nesting the ROR allows for single register compiler optimizations
#define S0(x) (ROR32(ROR32(ROR32(x,9)^(x),11)^(x),2)) // Σ0 (Sigma 0)
#define S1(x) (ROR32(ROR32(ROR32(x,14)^(x),5)^(x),6)) // Σ1 (Sigma 1)
#define g0(x) (ROR32(ROR32(x,11)^(x),7)^((x)>>3)) // γ0 (gamma 0)
#define g1(x) (ROR32(ROR32(x,2)^(x),17)^((x)>>10)) // γ1 (gamma 1)
#define BLK0(i) (x[i])
#define BLK2(i) (x[i] += g1(x[((i)-2)&15]) + x[((i)-7)&15] + g0(x[((i)-15)&15]))
#define R(a, b, c, d, e, f, g, h, i) \
h += S1(e) + Ch(e,f,g) + K256[(i)+(j)] + (j ? BLK2(i) : BLK0(i)); \
d += h; \
h += S0(a) + Ma(a, b, c)
#define RX_8(i) \
R(a, b, c, d, e, f, g, h, i); \
R(h, a, b, c, d, e, f, g, i+1); \
R(g, h, a, b, c, d, e, f, i+2); \
R(f, g, h, a, b, c, d, e, i+3); \
R(e, f, g, h, a, b, c, d, i+4); \
R(d, e, f, g, h, a, b, c, i+5); \
R(c, d, e, f, g, h, a, b, i+6); \
R(b, c, d, e, f, g, h, a, i+7)
#ifdef BIG_ENDIAN_HOST
memcpy(x, data, sizeof(x));
#else
{
uint32_t k;
for (k = 0; k < 16; k += 4) {
const uint8_t* p2 = data + k * 4;
x[k] = read_swap32(p2);
x[k + 1] = read_swap32(p2 + 4);
x[k + 2] = read_swap32(p2 + 8);
x[k + 3] = read_swap32(p2 + 12);
}
}
#endif
for (j = 0; j < 64; j += 16) {
RX_8(0);
RX_8(8);
}
#undef S0
#undef S1
#undef g0
#undef g1
#undef BLK0
#undef BLK2
#undef R
#undef RX_8
ctx->state[0] += a;
ctx->state[1] += b;
ctx->state[2] += c;
ctx->state[3] += d;
ctx->state[4] += e;
ctx->state[5] += f;
ctx->state[6] += g;
ctx->state[7] += h;
}
/* Transform the message X which consists of 16 32-bit-words (MD5) */
static void md5_transform(SUM_CONTEXT *ctx, const uint8_t *data)
{
uint32_t a, b, c, d, x[16];
a = ctx->state[0];
b = ctx->state[1];
c = ctx->state[2];
d = ctx->state[3];
#ifdef BIG_ENDIAN_HOST
{
uint32_t k;
for (k = 0; k < 16; k += 4) {
const uint8_t *p2 = data + k * 4;
x[k] = read_swap32(p2);
x[k + 1] = read_swap32(p2 + 4);
x[k + 2] = read_swap32(p2 + 8);
x[k + 3] = read_swap32(p2 + 12);
}
}
#else
memcpy(x, data, sizeof(x));
#endif
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
#define MD5STEP(f, w, x, y, z, data, s) do { \
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ); } while(0)
MD5STEP(F1, a, b, c, d, x[0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, x[1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, x[2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, x[3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, x[4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, x[5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, x[6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, x[7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, x[8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, x[9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, x[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, x[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, x[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, x[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, x[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, x[15] + 0x49b40821, 22);
MD5STEP(F2, a, b, c, d, x[1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, x[6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, x[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, x[0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, x[5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, x[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, x[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, x[4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, x[9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, x[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, x[3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, x[8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, x[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, x[2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, x[7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, x[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, x[5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, x[8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, x[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, x[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, x[1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, x[4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, x[7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, x[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, x[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, x[0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, x[3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, x[6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, x[9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, x[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, x[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, x[2] + 0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, x[0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, x[7] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, x[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, x[5] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, x[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, x[3] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, x[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, x[1] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, x[8] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, x[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, x[6] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, x[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, x[4] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, x[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, x[2] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, x[9] + 0xeb86d391, 21);
#undef F1
#undef F2
#undef F3
#undef F4
/* Update chaining vars */
ctx->state[0] += a;
ctx->state[1] += b;
ctx->state[2] += c;
ctx->state[3] += d;
}
/* Update the message digest with the contents of the buffer (SHA-1) */
static void sha1_write(SUM_CONTEXT *ctx, const uint8_t *buf, size_t len)
{
size_t num = ctx->bytecount & (SHA1_BLOCKSIZE - 1);
/* Update bytecount */
ctx->bytecount += len;
/* Handle any leading odd-sized chunks */
if (num) {
uint8_t *p = ctx->buf + num;
num = SHA1_BLOCKSIZE - num;
if (len < num) {
memcpy(p, buf, len);
return;
}
memcpy(p, buf, num);
sha1_transform(ctx, ctx->buf);
buf += num;
len -= num;
}
/* Process data in blocksize chunks */
while (len >= SHA1_BLOCKSIZE) {
PREFETCH64(buf + SHA1_BLOCKSIZE);
sha1_transform(ctx, buf);
buf += SHA1_BLOCKSIZE;
len -= SHA1_BLOCKSIZE;
}
/* Handle any remaining bytes of data. */
memcpy(ctx->buf, buf, len);
}
/* Update the message digest with the contents of the buffer (SHA-256) */
static void sha256_write(SUM_CONTEXT *ctx, const uint8_t *buf, size_t len)
{
size_t num = ctx->bytecount & (SHA256_BLOCKSIZE - 1);
/* Update bytecount */
ctx->bytecount += len;
/* Handle any leading odd-sized chunks */
if (num) {
uint8_t *p = ctx->buf + num;
num = SHA256_BLOCKSIZE - num;
if (len < num) {
memcpy(p, buf, len);
return;
}
memcpy(p, buf, num);
sha256_transform(ctx, ctx->buf);
buf += num;
len -= num;
}
/* Process data in blocksize chunks */
while (len >= SHA256_BLOCKSIZE) {
PREFETCH64(buf + SHA256_BLOCKSIZE);
sha256_transform(ctx, buf);
buf += SHA256_BLOCKSIZE;
len -= SHA256_BLOCKSIZE;
}
/* Handle any remaining bytes of data. */
memcpy(ctx->buf, buf, len);
}
/* Update the message digest with the contents of the buffer (MD5) */
static void md5_write(SUM_CONTEXT *ctx, const uint8_t *buf, size_t len)
{
size_t num = ctx->bytecount & (MD5_BLOCKSIZE - 1);
/* Update bytecount */
ctx->bytecount += len;
/* Handle any leading odd-sized chunks */
if (num) {
uint8_t *p = ctx->buf + num;
num = MD5_BLOCKSIZE - num;
if (len < num) {
memcpy(p, buf, num);
return;
}
memcpy(p, buf, num);
md5_transform(ctx, ctx->buf);
buf += num;
len -= num;
}
/* Process data in blocksize chunks */
while (len >= MD5_BLOCKSIZE) {
PREFETCH64(buf + MD5_BLOCKSIZE);
md5_transform(ctx, buf);
buf += MD5_BLOCKSIZE;
len -= MD5_BLOCKSIZE;
}
/* Handle any remaining bytes of data. */
memcpy(ctx->buf, buf, len);
}
/* Finalize the computation and write the digest in ctx->state[] (SHA-1) */
static void sha1_final(SUM_CONTEXT *ctx)
{
size_t pos = ((size_t)ctx->bytecount) & (SHA1_BLOCKSIZE - 1);
uint64_t bitcount = ctx->bytecount << 3;
uint8_t *p;
ctx->buf[pos++] = 0x80;
/* Pad whatever data is left in the buffer */
while (pos != (SHA1_BLOCKSIZE - 8)) {
pos &= (SHA1_BLOCKSIZE - 1);
if (pos == 0)
sha1_transform(ctx, ctx->buf);
ctx->buf[pos++] = 0;
}
/* Append to the padding the total message's length in bits and transform */
ctx->buf[SHA1_BLOCKSIZE - 1] = (uint8_t) bitcount;
ctx->buf[SHA1_BLOCKSIZE - 2] = (uint8_t) (bitcount >> 8);
ctx->buf[SHA1_BLOCKSIZE - 3] = (uint8_t) (bitcount >> 16);
ctx->buf[SHA1_BLOCKSIZE - 4] = (uint8_t) (bitcount >> 24);
ctx->buf[SHA1_BLOCKSIZE - 5] = (uint8_t) (bitcount >> 32);
ctx->buf[SHA1_BLOCKSIZE - 6] = (uint8_t) (bitcount >> 40);
ctx->buf[SHA1_BLOCKSIZE - 7] = (uint8_t) (bitcount >> 48);
ctx->buf[SHA1_BLOCKSIZE - 8] = (uint8_t) (bitcount >> 56);
sha1_transform(ctx, ctx->buf);
p = ctx->buf;
#ifdef BIG_ENDIAN_HOST
#define X(a) do { *(uint32_t*)p = ctx->state[a]; p += 4; } while(0)
#else
#define X(a) do { write_swap32(p, ctx->state[a]); p += 4; } while(0);
#endif
X(0);
X(1);
X(2);
X(3);
X(4);
#undef X
}
/* Finalize the computation and write the digest in ctx->state[] (SHA-256) */
static void sha256_final(SUM_CONTEXT *ctx)
{
size_t pos = ((size_t)ctx->bytecount) & (SHA256_BLOCKSIZE - 1);
uint64_t bitcount = ctx->bytecount << 3;
uint8_t *p;
ctx->buf[pos++] = 0x80;
/* Pad whatever data is left in the buffer */
while (pos != (SHA256_BLOCKSIZE - 8)) {
pos &= (SHA256_BLOCKSIZE - 1);
if (pos == 0)
sha256_transform(ctx, ctx->buf);
ctx->buf[pos++] = 0;
}
/* Append to the padding the total message's length in bits and transform */
ctx->buf[SHA256_BLOCKSIZE - 1] = (uint8_t) bitcount;
ctx->buf[SHA256_BLOCKSIZE - 2] = (uint8_t) (bitcount >> 8);
ctx->buf[SHA256_BLOCKSIZE - 3] = (uint8_t) (bitcount >> 16);
ctx->buf[SHA256_BLOCKSIZE - 4] = (uint8_t) (bitcount >> 24);
ctx->buf[SHA256_BLOCKSIZE - 5] = (uint8_t) (bitcount >> 32);
ctx->buf[SHA256_BLOCKSIZE - 6] = (uint8_t) (bitcount >> 40);
ctx->buf[SHA256_BLOCKSIZE - 7] = (uint8_t) (bitcount >> 48);
ctx->buf[SHA256_BLOCKSIZE - 8] = (uint8_t) (bitcount >> 56);
sha256_transform(ctx, ctx->buf);
p = ctx->buf;
#ifdef BIG_ENDIAN_HOST
#define X(a) do { *(uint32_t*)p = ctx->state[a]; p += 4; } while(0)
#else
#define X(a) do { write_swap32(p, ctx->state[a]); p += 4; } while(0);
#endif
X(0);
X(1);
X(2);
X(3);
X(4);
X(5);
X(6);
X(7);
#undef X
}
/* Finalize the computation and write the digest in ctx->state[] (MD5) */
static void md5_final(SUM_CONTEXT *ctx)
{
size_t count = ((size_t)ctx->bytecount) & (MD5_BLOCKSIZE - 1);
uint64_t bitcount = ctx->bytecount << 3;
uint8_t *p;
/* Set the first char of padding to 0x80.
* This is safe since there is always at least one byte free
*/
p = ctx->buf + count;
*p++ = 0x80;
/* Bytes of padding needed to make blocksize */
count = (MD5_BLOCKSIZE - 1) - count;
/* Pad out to 56 mod 64 */
if (count < 8) {
/* Two lots of padding: Pad the first block to blocksize */
memset(p, 0, count);
md5_transform(ctx, ctx->buf);
/* Now fill the next block */
memset(ctx->buf, 0, MD5_BLOCKSIZE - 8);
} else {
/* Pad block to blocksize */
memset(p, 0, count - 8);
}
/* append the 64 bit count (little endian) */
ctx->buf[MD5_BLOCKSIZE - 8] = (uint8_t) bitcount;
ctx->buf[MD5_BLOCKSIZE - 7] = (uint8_t) (bitcount >> 8);
ctx->buf[MD5_BLOCKSIZE - 6] = (uint8_t) (bitcount >> 16);
ctx->buf[MD5_BLOCKSIZE - 5] = (uint8_t) (bitcount >> 24);
ctx->buf[MD5_BLOCKSIZE - 4] = (uint8_t) (bitcount >> 32);
ctx->buf[MD5_BLOCKSIZE - 3] = (uint8_t) (bitcount >> 40);
ctx->buf[MD5_BLOCKSIZE - 2] = (uint8_t) (bitcount >> 48);
ctx->buf[MD5_BLOCKSIZE - 1] = (uint8_t) (bitcount >> 56);
md5_transform(ctx, ctx->buf);
p = ctx->buf;
#ifdef BIG_ENDIAN_HOST
#define X(a) do { write_swap32(p, ctx->state[a]); p += 4; } while(0);
#else
#define X(a) do { *(uint32_t*)p = ctx->state[a]; p += 4; } while(0)
#endif
X(0);
X(1);
X(2);
X(3);
#undef X
}
//#define NULL_TEST
#ifdef NULL_TEST
// These 'null' calls are useful for testing load balancing and individual algorithm speed
static void null_init(SUM_CONTEXT *ctx) { memset(ctx, 0, sizeof(*ctx)); }
static void null_write(SUM_CONTEXT *ctx, const uint8_t *buf, size_t len) { }
static void null_final(SUM_CONTEXT *ctx) { }
#endif
typedef void sum_init_t(SUM_CONTEXT *ctx);
typedef void sum_write_t(SUM_CONTEXT *ctx, const uint8_t *buf, size_t len);
typedef void sum_final_t(SUM_CONTEXT *ctx);
sum_init_t *sum_init[CHECKSUM_MAX] = { md5_init, sha1_init , sha256_init };
sum_write_t *sum_write[CHECKSUM_MAX] = { md5_write, sha1_write , sha256_write };
sum_final_t *sum_final[CHECKSUM_MAX] = { md5_final, sha1_final , sha256_final };
// Compute an individual checksum without threading or buffering, for a single file
BOOL HashFile(const unsigned type, const char* path, uint8_t* sum)
{
BOOL r = FALSE;
SUM_CONTEXT sum_ctx = { {0} };
HANDLE h = INVALID_HANDLE_VALUE;
DWORD rs = 0;
uint64_t rb;
uint8_t buf[4096];
if ((type >= CHECKSUM_MAX) || (path == NULL) || (sum == NULL))
goto out;
h = CreateFileU(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (h == INVALID_HANDLE_VALUE) {
uprintf("Could not open file: %s", WindowsErrorString());
FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_OPEN_FAILED;
goto out;
}
sum_init[type](&sum_ctx);
for (rb = 0; ; rb += rs) {
CHECK_FOR_USER_CANCEL;
if (!ReadFile(h, buf, sizeof(buf), &rs, NULL)) {
FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_READ_FAULT;
uprintf(" Read error: %s", WindowsErrorString());
goto out;
}
if (rs == 0)
break;
sum_write[type](&sum_ctx, buf, (size_t)rs);
}
sum_final[type](&sum_ctx);
memcpy(sum, sum_ctx.buf, sum_count[type]);
r = TRUE;
out:
safe_closehandle(h);
return r;
}
BOOL HashBuffer(const unsigned type, const uint8_t* buf, const size_t len, uint8_t* sum)
{
BOOL r = FALSE;
SUM_CONTEXT sum_ctx = { {0} };
if ((type >= CHECKSUM_MAX) || (sum == NULL))
goto out;
sum_init[type](&sum_ctx);
sum_write[type](&sum_ctx, buf, len);
sum_final[type](&sum_ctx);
memcpy(sum, sum_ctx.buf, sum_count[type]);
r = TRUE;
out:
return r;
}
/*
* Checksum dialog callback
*/
INT_PTR CALLBACK ChecksumCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
int i, dw, dh;
RECT rc;
HFONT hFont;
HDC hDC;
switch (message) {
case WM_INITDIALOG:
apply_localization(IDD_CHECKSUM, hDlg);
hDC = GetDC(hDlg);
hFont = CreateFontA(-MulDiv(9, GetDeviceCaps(hDC, LOGPIXELSY), 72),
0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
0, 0, PROOF_QUALITY, 0, "Courier New");
safe_release_dc(hDlg, hDC);
SendDlgItemMessageA(hDlg, IDC_MD5, WM_SETFONT, (WPARAM)hFont, TRUE);
SendDlgItemMessageA(hDlg, IDC_SHA1, WM_SETFONT, (WPARAM)hFont, TRUE);
SendDlgItemMessageA(hDlg, IDC_SHA256, WM_SETFONT, (WPARAM)hFont, TRUE);
SetWindowTextA(GetDlgItem(hDlg, IDC_MD5), sum_str[0]);
SetWindowTextA(GetDlgItem(hDlg, IDC_SHA1), sum_str[1]);
SetWindowTextA(GetDlgItem(hDlg, IDC_SHA256), sum_str[2]);
// Move/Resize the controls as needed to fit our text
hDC = GetDC(GetDlgItem(hDlg, IDC_MD5));
SelectFont(hDC, hFont); // Yes, you *MUST* reapply the font to the DC, even after SetWindowText!
GetWindowRect(GetDlgItem(hDlg, IDC_MD5), &rc);
dw = rc.right - rc.left;
dh = rc.bottom - rc.top;
DrawTextU(hDC, sum_str[0], -1, &rc, DT_CALCRECT);
dw = rc.right - rc.left - dw + 12; // Ideally we'd compute the field borders from the system, but hey...
dh = rc.bottom - rc.top - dh + 6;
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDC_SHA256), 0, 0, dw, dh, 1.0f);
GetWindowRect(GetDlgItem(hDlg, IDC_SHA1), &rc);
dw = rc.right - rc.left;
DrawTextU(hDC, sum_str[1], -1, &rc, DT_CALCRECT);
dw = rc.right - rc.left - dw + 12;
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDC_MD5), 0, 0, dw, 0, 1.0f);
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDC_SHA1), 0, 0, dw, 0, 1.0f);
ResizeButtonHeight(hDlg, IDOK);
safe_release_dc(GetDlgItem(hDlg, IDC_MD5), hDC);
for (i=(int)safe_strlen(image_path); (i>0)&&(image_path[i]!='\\'); i--);
if (image_path != NULL) // VS code analysis has a false positive on this one
SetWindowTextU(hDlg, &image_path[i+1]);
// Set focus on the OK button
SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, IDOK), TRUE);
CenterDialog(hDlg, NULL);
break;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
case IDCANCEL:
reset_localization(IDD_CHECKSUM);
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
}
return (INT_PTR)FALSE;
}
// Individual thread that computes one of MD5, SHA1 or SHA256 in parallel
DWORD WINAPI IndividualSumThread(void* param)
{
SUM_CONTEXT sum_ctx = { {0} }; // There's a memset in sum_init, but static analyzers still bug us
uint32_t i = (uint32_t)(uintptr_t)param, j;
sum_init[i](&sum_ctx);
// Signal that we're ready to service requests
if (!SetEvent(thread_ready[i]))
goto error;
// Wait for requests
while (1) {
if (WaitForSingleObject(data_ready[i], WAIT_TIME) != WAIT_OBJECT_0) {
uprintf("Failed to wait for event for checksum thread #%d: %s", i, WindowsErrorString());
return 1;
}
if (read_size[bufnum] != 0) {
sum_write[i](&sum_ctx, buffer[bufnum], (size_t)read_size[bufnum]);
if (!SetEvent(thread_ready[i]))
goto error;
} else {
sum_final[i](&sum_ctx);
memset(&sum_str[i], 0, ARRAYSIZE(sum_str[i]));
for (j = 0; j < sum_count[i]; j++)
safe_sprintf(&sum_str[i][2 * j], ARRAYSIZE(sum_str[i]) - 2 * j, "%02x", sum_ctx.buf[j]);
return 0;
}
}
error:
uprintf("Failed to set event for checksum thread #%d: %s", i, WindowsErrorString());
return 1;
}
DWORD WINAPI SumThread(void* param)
{
DWORD_PTR* thread_affinity = (DWORD_PTR*)param;
HANDLE sum_thread[CHECKSUM_MAX] = { NULL, NULL, NULL };
HANDLE h = INVALID_HANDLE_VALUE;
uint64_t rb;
int i, _bufnum, r = -1;
if ((image_path == NULL) || (thread_affinity == NULL))
ExitThread(r);
uprintf("\r\nComputing checksum for '%s'...", image_path);
if (thread_affinity[0] != 0)
// Use the first affinity mask, as our read thread is the least
// CPU intensive (mostly waits on disk I/O or on the other threads)
// whereas the OS is likely to requisition the first Core, which
// is usually in this first mask, for other tasks.
SetThreadAffinityMask(GetCurrentThread(), thread_affinity[0]);
for (i = 0; i < CHECKSUM_MAX; i++) {
// NB: Can't use a single manual-reset event for data_ready as we
// wouldn't be able to ensure the event is reset before the thread
// gets into its next wait loop
data_ready[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
thread_ready[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
if ((data_ready[i] == NULL) || (thread_ready[i] == NULL)) {
uprintf("Unable to create checksum thread event: %s", WindowsErrorString());
goto out;
}
sum_thread[i] = CreateThread(NULL, 0, IndividualSumThread, (LPVOID)(uintptr_t)i, 0, NULL);
if (sum_thread[i] == NULL) {
uprintf("Unable to start checksum thread #%d", i);
goto out;
}
if (thread_affinity[i+1] != 0)
SetThreadAffinityMask(sum_thread[i], thread_affinity[i+1]);
}
h = CreateFileU(image_path, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (h == INVALID_HANDLE_VALUE) {
uprintf("Could not open file: %s", WindowsErrorString());
FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_OPEN_FAILED;
goto out;
}
bufnum = 0;
_bufnum = 0;
read_size[0] = 1; // Don't trigger the first loop break
UpdateProgressWithInfoInit(hMainDialog, FALSE);
for (rb = 0; ;rb += read_size[_bufnum]) {
// Update the progress and check for cancel
UpdateProgressWithInfo(OP_NOOP_WITH_TASKBAR, MSG_271, rb, img_report.image_size);
CHECK_FOR_USER_CANCEL;
// Signal the threads that we have data to process
if (rb != 0) {
bufnum = _bufnum;
// Toggle the read buffer
_bufnum = (bufnum + 1) % 2;
// Signal the waiting threads
for (i = 0; i < CHECKSUM_MAX; i++) {
if (!SetEvent(data_ready[i])) {
uprintf("Could not signal checksum thread %d: %s", i, WindowsErrorString());
goto out;
}
}
}
// Break the loop when data has been exhausted
if (read_size[bufnum] == 0)
break;
// Read data (double buffered)
if (!ReadFile(h, buffer[_bufnum], BUFFER_SIZE, &read_size[_bufnum], NULL)) {
FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_READ_FAULT;
uprintf("Read error: %s", WindowsErrorString());
goto out;
}
// Wait for the thread to signal they are ready to process data
if (WaitForMultipleObjects(CHECKSUM_MAX, thread_ready, TRUE, WAIT_TIME) != WAIT_OBJECT_0) {
uprintf("Checksum threads failed to signal: %s", WindowsErrorString());
goto out;
}
}
// Our last event with read_size=0 signaled the threads to exit - wait for that to happen
if (WaitForMultipleObjects(CHECKSUM_MAX, sum_thread, TRUE, WAIT_TIME) != WAIT_OBJECT_0) {
uprintf("Checksum threads did not finalize: %s", WindowsErrorString());
goto out;
}
uprintf(" MD5:\t %s", sum_str[0]);
uprintf(" SHA1:\t %s", sum_str[1]);
uprintf(" SHA256: %s", sum_str[2]);
r = 0;
out:
for (i = 0; i < CHECKSUM_MAX; i++) {
if (sum_thread[i] != NULL)
TerminateThread(sum_thread[i], 1);
safe_closehandle(data_ready[i]);
safe_closehandle(thread_ready[i]);
}
safe_closehandle(h);
PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)FALSE, 0);
if (r == 0)
MyDialogBox(hMainInstance, IDD_CHECKSUM, hMainDialog, ChecksumCallback);
ExitThread(r);
}
/*
* The following 2 calls are used to check whether a buffer/file is in our hash DB
*/
BOOL IsBufferInDB(const unsigned char* buf, const size_t len)
{
int i;
uint8_t sum[32];
if (!HashBuffer(CHECKSUM_SHA256, buf, len, sum))
return FALSE;
for (i = 0; i < ARRAYSIZE(sha256db); i += 32)
if (memcmp(sum, &sha256db[i], 32) == 0)
return TRUE;
return FALSE;
}
BOOL IsFileInDB(const char* path)
{
int i;
uint8_t sum[32];
if (!HashFile(CHECKSUM_SHA256, path, sum))
return FALSE;
for (i = 0; i < ARRAYSIZE(sha256db); i += 32)
if (memcmp(sum, &sha256db[i], 32) == 0)
return TRUE;
return FALSE;
}