Initial shot at writing an ioquake3 autoupdater.

This commit is contained in:
Ryan C. Gordon 2017-05-25 14:13:18 -04:00
parent c65d2c2657
commit 4729c683fd
6 changed files with 1147 additions and 22 deletions

View file

@ -0,0 +1,721 @@
/*
The code in this file is in the public domain. The rest of ioquake3
is licensed until the GPLv2. Do not mingle code, please!
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/stat.h>
#include <signal.h>
#include <curl/curl.h>
#include "sha256.h"
#ifndef AUTOUPDATE_USER_AGENT
#define AUTOUPDATE_USER_AGENT "ioq3autoupdater/0.1"
#endif
#ifndef AUTOUPDATE_URL
#ifndef AUTOUPDATE_BASEURL
#define AUTOUPDATE_BASEURL "https://upd.ioquake3.org/updates/v1"
#endif
#ifndef AUTOUPDATE_PACKAGE
#define AUTOUPDATE_PACKAGE "ioquake3"
#endif
#ifdef __APPLE__
#define AUTOUPDATE_PLATFORM "mac"
#elif defined(__linux__)
#define AUTOUPDATE_PLATFORM "linux"
#else
#error Please define your platform.
#endif
#ifdef __i386__
#define AUTOUPDATE_ARCH "i386"
#elif defined(__x86_64__)
#define AUTOUPDATE_ARCH "x86-64"
#else
#error Please define your platform.
#endif
#define AUTOUPDATE_URL AUTOUPDATE_BASEURL "/" AUTOUPDATE_PACKAGE "/" AUTOUPDATE_PLATFORM "/" AUTOUPDATE_ARCH "/"
#endif
#if defined(__GNUC__) || defined(__clang__)
#define NEVER_RETURNS __attribute__((noreturn))
#else
#define NEVER_RETURNS
#endif
typedef struct
{
pid_t waitforprocess;
const char *updateself;
} Options;
static Options options;
typedef struct ManifestItem
{
char *fname;
unsigned char sha256[32];
int64_t len;
int update;
int rollback;
struct ManifestItem *next;
} ManifestItem;
static ManifestItem *manifest;
static void freeManifest(void)
{
ManifestItem *item = manifest;
manifest = NULL;
while (item != NULL) {
ManifestItem *next = item->next;
free(item->fname);
free(item);
item = next;
}
manifest = NULL;
}
static FILE *logfile = NULL;
#define SDL_PRINTF_VARARG_FUNC( fmtargnumber ) __attribute__ (( format( __printf__, fmtargnumber, fmtargnumber+1 )))
static void info(const char *str)
{
fputs(str, logfile);
fputs("\n", logfile);
fflush(logfile);
}
static void infof(const char *fmt, ...)
#if defined(__GNUC__) || defined(__clang__)
__attribute__ (( format( __printf__, 1, 2 )))
#endif
;
static void infof(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(logfile, fmt, ap);
va_end(ap);
fputs("\n", logfile);
fflush(logfile);
}
static void restoreRollbacks(void)
{
/* you can't call die() in this function! If this fails, you're doomed. */
ManifestItem *item;
for (item = manifest; item != NULL; item = item->next) {
if (item->rollback) {
char rollbackPath[64];
snprintf(rollbackPath, sizeof (rollbackPath), "updates/rollbacks/%d", item->rollback);
infof("restore rollback: '%s' -> '%s'", rollbackPath, item->fname);
remove(item->fname);
rename(rollbackPath, item->fname);
}
}
}
static void die(const char *why) NEVER_RETURNS;
static void die(const char *why)
{
infof("FAILURE: %s", why);
restoreRollbacks();
freeManifest();
exit(1);
}
static void outOfMemory() NEVER_RETURNS;
static void outOfMemory()
{
die("Out of memory");
}
static void makeDir(const char *dirname)
{
/* !!! FIXME: we don't care if this fails right now. */
mkdir(dirname, 0777);
}
static void buildParentDirs(const char *_path)
{
char *ptr;
char *path = (char *) alloca(strlen(_path) + 1);
if (!path) {
outOfMemory();
}
strcpy(path, _path);
for (ptr = path; *ptr; ptr++) {
if (*ptr == '/') {
*ptr = '\0';
makeDir(path);
*ptr = '/';
}
}
}
static int64_t fileLength(const char *fname)
{
struct stat statbuf;
if (stat(fname, &statbuf) == -1) {
return -1;
}
return (int64_t) statbuf.st_size;
}
static void parseArgv(int argc, char **argv)
{
int i;
infof("command line (argc=%d)...", argc);
for (i = 0; i < argc; i++) {
infof(" argv[%d]: %s",i, argv[i]);
}
for (i = 1; i < argc; i += 2) {
if (strcmp(argv[i], "--waitpid") == 0) {
options.waitforprocess = atoll(argv[i + 1]);
infof("We will wait for process %lld if necessary", (long long) options.waitforprocess);
} else if (strcmp(argv[i], "--updateself") == 0) {
options.updateself = argv[i + 1];
infof("We are updating ourself ('%s')", options.updateself);
}
}
}
static CURL *prepCurl(const char *url, FILE *outfile)
{
char *fullurl;
const size_t len = strlen(AUTOUPDATE_URL) + strlen(url) + 1;
CURL *curl = curl_easy_init();
if (!curl) {
die("curl_easy_init() failed");
}
fullurl = (char *) alloca(len);
if (!fullurl) {
outOfMemory();
}
snprintf(fullurl, len, "%s%s", AUTOUPDATE_URL, url);
infof("Downloading from '%s'", fullurl);
#if 0
/* !!! FIXME: enable compression? */
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""); /* enable compression */
/* !!! FIXME; hook up proxy support to libcurl */
curl_easy_setopt(curl, CURLOPT_PROXY, proxyURL);
#endif
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(curl, CURLOPT_STDERR, logfile);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
curl_easy_setopt(curl, CURLOPT_URL, fullurl);
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); /* allow redirects. */
curl_easy_setopt(curl, CURLOPT_USERAGENT, AUTOUPDATE_USER_AGENT);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); /* require valid SSL cert. */
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); /* require SSL cert with same hostname as we connected to. */
return curl;
}
static void downloadURL(const char *from, const char *to)
{
FILE *io;
CURL *curl;
infof("Preparing to download to '%s'", to);
buildParentDirs(to);
io = fopen(to, "wb");
if (!io) {
die("Failed to open output file");
}
curl = prepCurl(from, io);
if (curl_easy_perform(curl) != CURLE_OK) {
remove(to);
die("Download failed");
}
curl_easy_cleanup(curl);
if (fclose(io) == EOF) {
die("Can't flush file on close. i/o error? Disk full?");
}
chmod(to, 0777); /* !!! FIXME */
}
static int hexcvt(const int ch)
{
if ((ch >= 'a') && (ch <= 'f')) {
return (ch - 'a') + 10;
} else if ((ch >= 'A') && (ch <= 'F')) {
return (ch - 'A') + 10;
} else if ((ch >= '0') && (ch <= '9')) {
return ch - '0';
} else {
die("Invalid hex character");
}
return 0;
}
static void convertSha256(char *str, BYTE *sha256)
{
int i;
for (i = 0; i < 32; i++) {
const int a = hexcvt(*(str++));
const int b = hexcvt(*(str++));
*sha256 = (a << 4) | b;
sha256++;
}
}
static void parseManifest(const char *fname)
{
ManifestItem *item = NULL;
FILE *io = fopen(fname, "r");
char buf[512];
if (!io) {
die("Failed to open manifest for reading");
}
/* !!! FIXME: this code sucks. */
while (fgets(buf, sizeof (buf), io)) {
char *ptr = (buf + strlen(buf)) - 1;
while (ptr >= buf) {
if ((*ptr != '\n') && (*ptr != '\r')) {
break;
}
*ptr = '\0';
ptr--;
}
if (!item && !buf[0]) {
continue; /* blank line between items or blank at EOF */
}
if (!item) {
infof("Next manifest item: %s", buf);
item = (ManifestItem *) malloc(sizeof (ManifestItem));
if (!item) {
outOfMemory();
}
item->fname = strdup(buf);
if (!item->fname) {
outOfMemory();
}
item->len = -1;
item->next = NULL;
} else if (item->len == -1) {
infof("Item size: %s", buf);
item->len = atoll(buf);
} else {
infof("Item sha256: %s", buf);
convertSha256(buf, item->sha256);
item->next = manifest;
manifest = item;
item = NULL;
}
}
if (ferror(io)) {
die("Error reading manifest");
} else if (item) {
die("Incomplete manifest");
}
fclose(io);
}
static void downloadManifest(void)
{
const char *manifestfname = "updates/manifest.txt";
downloadURL("manifest.txt", manifestfname);
/* !!! FIXME: verify manifest download is complete... */
parseManifest(manifestfname);
}
static void upgradeSelfAndRestart(const char *argv0) NEVER_RETURNS;
static void upgradeSelfAndRestart(const char *argv0)
{
const char *tempfname = "origUpdater";
const char *why = NULL;
FILE *in = NULL;
FILE *out = NULL;
in = fopen(argv0, "rb");
if (!in) {
die("Can't open self for input while upgrading updater");
}
remove(tempfname);
if (rename(options.updateself, tempfname) == -1) {
die("Can't rename original while upgrading updater");
}
out = fopen(options.updateself, "wb");
if (!out) {
die("Can't open file for output while upgrading updater");
}
while (!feof(in) && !why) {
char buf[512];
const size_t br = fread(buf, 1, sizeof (buf), in);
if (br > 0) {
if (fwrite(buf, br, 1, out) != 1) {
why = "write failure while upgrading updater";
}
} else if (ferror(in)) {
why = "read failure while upgrading updater";
}
}
fclose(in);
if ((fclose(out) == EOF) && (!why)) {
why = "close failure while upgrading updater";
}
if (why) {
remove(options.updateself);
rename(tempfname, options.updateself);
die(why);
}
remove(tempfname);
chmod(options.updateself, 0777);
if (options.waitforprocess) {
char pidstr[64];
snprintf(pidstr, sizeof (pidstr), "%lld", (long long) options.waitforprocess);
execl(options.updateself, options.updateself, "--waitpid", pidstr, NULL);
} else {
execl(options.updateself, options.updateself, NULL);
}
die("Failed to relaunch upgraded updater");
}
static const char *justFilename(const char *path)
{
const char *fname = strrchr(path, '/');
return fname ? fname + 1 : path;
}
static void hashFile(const char *fname, unsigned char *sha256)
{
SHA256_CTX sha256ctx;
BYTE buf[512];
FILE *io;
io = fopen(fname, "rb");
if (!io) {
die("Failed to open file for hashing");
}
sha256_init(&sha256ctx);
do {
size_t br = fread(buf, 1, sizeof (buf), io);
if (br > 0) {
sha256_update(&sha256ctx, buf, br);
}
if (ferror(io)) {
die("Error reading file for hashing");
}
} while (!feof(io));
fclose(io);
sha256_final(&sha256ctx, sha256);
}
static int fileHashMatches(const char *fname, const unsigned char *wanted)
{
unsigned char sha256[32];
hashFile(fname, sha256);
return (memcmp(sha256, wanted, 32) == 0);
}
static int fileNeedsUpdate(const ManifestItem *item)
{
if (item->len != fileLength(item->fname)) {
infof("Update '%s', file size is different", item->fname);
return 1; /* obviously different. */
} else if (!fileHashMatches(item->fname, item->sha256)) {
infof("Update '%s', file sha256 is different", item->fname);
return 1;
}
infof("Don't update '%s', the file is already up to date", item->fname);
return 0;
}
static void downloadFile(const ManifestItem *item)
{
const char *outpath = "updates/downloads/";
const size_t len = strlen(outpath) + strlen(item->fname) + 1;
char *to = (char *) alloca(len);
if (!to) {
outOfMemory();
}
snprintf(to, len, "%s%s", outpath, item->fname);
if ((item->len == fileLength(to)) && fileHashMatches(to, item->sha256)) {
infof("Already downloaded '%s', not getting again", item->fname);
} else {
downloadURL(item->fname, to);
if ((item->len != fileLength(to)) || !fileHashMatches(to, item->sha256)) {
die("Download is incorrect or corrupted");
}
}
}
static int downloadUpdates(void)
{
int updatesAvailable = 0;
ManifestItem *item;
for (item = manifest; item != NULL; item = item->next) {
item->update = fileNeedsUpdate(item);
if (item->update) {
updatesAvailable = 1;
downloadFile(item);
}
}
return updatesAvailable;
}
static void maybeUpdateSelf(const char *argv0)
{
ManifestItem *item;
/* !!! FIXME: this needs to be a different string on macOS. */
const char *fname = justFilename(argv0);
for (item = manifest; item != NULL; item = item->next) {
if (strcasecmp(item->fname, fname) == 0) {
if (fileNeedsUpdate(item)) {
const char *outpath = "updates/downloads/";
const size_t len = strlen(outpath) + strlen(item->fname) + 1;
char *to = (char *) alloca(len);
if (!to) {
outOfMemory();
}
snprintf(to, len, "%s%s", outpath, item->fname);
info("Have to upgrade the updater");
downloadFile(item);
chmod(to, 0777);
if (options.waitforprocess) {
char pidstr[64];
snprintf(pidstr, sizeof (pidstr), "%lld", (long long) options.waitforprocess);
execl(to, to, "--updateself", argv0, "--waitpid", pidstr, NULL);
} else {
execl(to, to, "--updateself", argv0, NULL);
}
die("Failed to initially launch upgraded updater");
}
break; /* done in any case. */
}
}
}
static void installUpdatedFile(const ManifestItem *item)
{
const char *basepath = "updates/downloads/";
const size_t len = strlen(basepath) + strlen(item->fname) + 1;
char *downloadPath = (char *) alloca(len);
if (!downloadPath) {
outOfMemory();
}
snprintf(downloadPath, len, "%s%s", basepath, item->fname);
infof("Moving file for update: '%s' -> '%s'", downloadPath, item->fname);
buildParentDirs(item->fname);
if (rename(downloadPath, item->fname) == -1) {
die("Failed to move updated file to final position");
}
}
static void applyUpdates(void)
{
FILE *io;
ManifestItem *item;
for (item = manifest; item != NULL; item = item->next) {
if (!item->update) {
continue;
}
io = fopen(item->fname, "rb");
fclose(io);
if (io != NULL) {
static int rollbackIndex = 0;
char rollbackPath[64];
item->rollback = ++rollbackIndex;
snprintf(rollbackPath, sizeof (rollbackPath), "updates/rollbacks/%d", rollbackIndex);
infof("Moving file for rollback: '%s' -> '%s'", item->fname, rollbackPath);
remove(rollbackPath);
if (rename(item->fname, rollbackPath) == -1) {
die("failed to move to rollback dir");
}
}
installUpdatedFile(item);
}
}
static void waitToApplyUpdates(void)
{
if (options.waitforprocess) {
/* ioquake3 opens a pipe on fd 3, and then forgets about it. We block
on a read to that pipe here. When the game process quits (and the
OS forcibly closes the pipe), we will unblock. Then we can loop on
kill() until the process is truly gone. */
int x = 0;
infof("Waiting for pid %lld to die...", (long long) options.waitforprocess);
read(3, &x, sizeof (x));
info("Pipe has closed, waiting for process to fully go away now.");
while (kill(options.waitforprocess, 0) == 0) {
usleep(100000);
}
info("pid is gone, continuing");
}
}
static void deleteRollbacks(void)
{
ManifestItem *item;
for (item = manifest; item != NULL; item = item->next) {
if (item->rollback) {
char rollbackPath[64];
snprintf(rollbackPath, sizeof (rollbackPath), "updates/rollbacks/%d", item->rollback);
infof("delete rollback: %s", rollbackPath);
remove(rollbackPath);
}
}
}
static const char *timestamp(void)
{
time_t t = time(NULL);
char *retval = asctime(localtime(&t));
if (retval) {
char *ptr;
for (ptr = retval; *ptr; ptr++) {
if ((*ptr == '\r') || (*ptr == '\n')) {
*ptr = '\0';
break;
}
}
}
return retval ? retval : "[date unknown]";
}
static void chdirToBasePath(const char *argv0)
{
const char *fname = justFilename(argv0);
size_t len;
char *buf;
if (fname == argv0) { /* no path? Assume we're already there. */
return;
}
len = ((size_t) (fname - argv0)) - 1;
buf = (char *) alloca(len);
if (!buf) {
outOfMemory();
}
memcpy(buf, argv0, len);
buf[len] = '\0';
if (chdir(buf) == -1) {
infof("base path is '%s'", buf);
die("chdir to base path failed");
}
}
int main(int argc, char **argv)
{
signal(SIGPIPE, SIG_IGN); /* don't trigger signal when fd3 closes */
logfile = stdout;
chdirToBasePath(argv[0]);
makeDir("updates");
makeDir("updates/downloads");
makeDir("updates/rollbacks");
logfile = fopen("updates/updater-log.txt", "a");
if (!logfile) {
logfile = stdout;
}
infof("Updater starting, %s", timestamp());
parseArgv(argc, argv);
/* if we have downloaded a new updater and restarted with that binary,
replace the original updater and restart again in the right place. */
if (options.updateself) {
upgradeSelfAndRestart(argv[0]);
}
if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) {
die("curl_global_init() failed!");
}
downloadManifest(); /* see if we need an update at all. */
maybeUpdateSelf(argv[0]); /* might relaunch if there's an updater upgrade. */
if (!downloadUpdates()) {
info("Nothing needs updating, so we're done here!");
} else {
waitToApplyUpdates();
applyUpdates();
deleteRollbacks();
info("You are now up to date!");
}
freeManifest();
curl_global_cleanup();
infof("Updater ending, %s", timestamp());
return 0;
}

158
code/autoupdater/sha256.c Normal file
View file

@ -0,0 +1,158 @@
/*********************************************************************
* Filename: sha256.c
* Author: Brad Conte (brad AT bradconte.com)
* Copyright:
* Disclaimer: This code is presented "as is" without any guarantees.
* Details: Implementation of the SHA-256 hashing algorithm.
SHA-256 is one of the three algorithms in the SHA2
specification. The others, SHA-384 and SHA-512, are not
offered in this implementation.
Algorithm specification can be found here:
* http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf
This implementation uses little endian byte order.
*********************************************************************/
/*************************** HEADER FILES ***************************/
#include <stdlib.h>
#include <memory.h>
#include "sha256.h"
/****************************** MACROS ******************************/
#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b))))
#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b))))
#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))
#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))
#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))
#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))
/**************************** VARIABLES *****************************/
static const WORD k[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
};
/*********************** FUNCTION DEFINITIONS ***********************/
void sha256_transform(SHA256_CTX *ctx, const BYTE data[])
{
WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
for (i = 0, j = 0; i < 16; ++i, j += 4)
m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
for ( ; i < 64; ++i)
m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 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];
for (i = 0; i < 64; ++i) {
t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i];
t2 = EP0(a) + MAJ(a,b,c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
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;
}
void sha256_init(SHA256_CTX *ctx)
{
ctx->datalen = 0;
ctx->bitlen = 0;
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;
}
void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len)
{
WORD i;
for (i = 0; i < len; ++i) {
ctx->data[ctx->datalen] = data[i];
ctx->datalen++;
if (ctx->datalen == 64) {
sha256_transform(ctx, ctx->data);
ctx->bitlen += 512;
ctx->datalen = 0;
}
}
}
void sha256_final(SHA256_CTX *ctx, BYTE hash[])
{
WORD i;
i = ctx->datalen;
// Pad whatever data is left in the buffer.
if (ctx->datalen < 56) {
ctx->data[i++] = 0x80;
while (i < 56)
ctx->data[i++] = 0x00;
}
else {
ctx->data[i++] = 0x80;
while (i < 64)
ctx->data[i++] = 0x00;
sha256_transform(ctx, ctx->data);
memset(ctx->data, 0, 56);
}
// Append to the padding the total message's length in bits and transform.
ctx->bitlen += ctx->datalen * 8;
ctx->data[63] = ctx->bitlen;
ctx->data[62] = ctx->bitlen >> 8;
ctx->data[61] = ctx->bitlen >> 16;
ctx->data[60] = ctx->bitlen >> 24;
ctx->data[59] = ctx->bitlen >> 32;
ctx->data[58] = ctx->bitlen >> 40;
ctx->data[57] = ctx->bitlen >> 48;
ctx->data[56] = ctx->bitlen >> 56;
sha256_transform(ctx, ctx->data);
// Since this implementation uses little endian byte ordering and SHA uses big endian,
// reverse all the bytes when copying the final state to the output hash.
for (i = 0; i < 4; ++i) {
hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
}
}

34
code/autoupdater/sha256.h Normal file
View file

@ -0,0 +1,34 @@
/*********************************************************************
* Filename: sha256.h
* Author: Brad Conte (brad AT bradconte.com)
* Copyright:
* Disclaimer: This code is presented "as is" without any guarantees.
* Details: Defines the API for the corresponding SHA1 implementation.
*********************************************************************/
#ifndef SHA256_H
#define SHA256_H
/*************************** HEADER FILES ***************************/
#include <stddef.h>
/****************************** MACROS ******************************/
#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest
/**************************** DATA TYPES ****************************/
typedef unsigned char BYTE; // 8-bit byte
typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines
typedef struct {
BYTE data[64];
WORD datalen;
unsigned long long bitlen;
WORD state[8];
} SHA256_CTX;
/*********************** FUNCTION DECLARATIONS **********************/
void sha256_init(SHA256_CTX *ctx);
void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len);
void sha256_final(SHA256_CTX *ctx, BYTE hash[]);
#endif // SHA256_H