/*
* checksum.c
*
* Copyright (c) 2008- Steve McIntyre <steve@einval.com>
*
* Implementation of a generic checksum interface, used in JTE.
*
* GNU GPL v2
*/
#include <mconfig.h>
#include "genisoimage.h"
#include <timedefs.h>
#include <fctldefs.h>
#include <regex.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "md5.h"
#include "sha1.h"
#include "sha256.h"
#include "sha512.h"
#include "checksum.h"
#ifdef THREADED_CHECKSUMS
# include <pthread.h>
#endif
static void md5_init(void *context)
{
mk_MD5Init(context);
}
static void md5_update(void *context, unsigned char const *buf, unsigned int len)
{
mk_MD5Update(context, buf, len);
}
static void md5_final(unsigned char *digest, void *context)
{
mk_MD5Final(digest, context);
}
static void sha1_init(void *context)
{
sha1_init_ctx(context);
}
static void sha1_update(void *context, unsigned char const *buf, unsigned int len)
{
sha1_process_bytes(buf, len, context);
}
static void sha1_final(unsigned char *digest, void *context)
{
sha1_finish_ctx(context, digest);
}
static void sha256_init(void *context)
{
sha256_init_ctx(context);
}
static void sha256_update(void *context, unsigned char const *buf, unsigned int len)
{
sha256_process_bytes(buf, len, context);
}
static void sha256_final(unsigned char *digest, void *context)
{
sha256_finish_ctx(context, digest);
}
static void sha512_init(void *context)
{
sha512_init_ctx(context);
}
static void sha512_update(void *context, unsigned char const *buf, unsigned int len)
{
sha512_process_bytes(buf, len, context);
}
static void sha512_final(unsigned char *digest, void *context)
{
sha512_finish_ctx(context, digest);
}
struct checksum_details
{
char *name;
char *prog;
int digest_size;
int context_size;
void (*init)(void *context);
void (*update)(void *context, unsigned char const *buf, unsigned int len);
void (*final)(unsigned char *digest, void *context);
};
static const struct checksum_details algorithms[] =
{
{
"MD5",
"md5sum",
16,
sizeof(struct mk_MD5Context),
md5_init,
md5_update,
md5_final
},
{
"SHA1",
"sha1sum",
20,
sizeof(struct sha1_ctx),
sha1_init,
sha1_update,
sha1_final
},
{
"SHA256",
"sha256sum",
32,
sizeof(struct sha256_ctx),
sha256_init,
sha256_update,
sha256_final
},
{
"SHA512",
"sha512sum",
64,
sizeof(struct sha512_ctx),
sha512_init,
sha512_update,
sha512_final
}
};
struct algo_context
{
void *context;
unsigned char *digest;
int enabled;
int finalised;
char *hexdump;
#ifdef THREADED_CHECKSUMS
unsigned char const *buf;
unsigned int len;
int which;
pthread_t thread;
struct _checksum_context *parent;
pthread_mutex_t start_mutex;
pthread_cond_t start_cv;
#endif
};
struct _checksum_context
{
#ifdef THREADED_CHECKSUMS
unsigned int index;
unsigned int threads_running;
unsigned int threads_desired;
pthread_mutex_t done_mutex;
pthread_cond_t done_cv;
#endif
char *owner;
struct algo_context algo[NUM_CHECKSUMS];
};
struct checksum_info *checksum_information(enum checksum_types which)
{
return (struct checksum_info *)&algorithms[which];
}
/* Dump a buffer in hex */
static void hex_dump_to_buffer(char *output_buffer, unsigned char *buf, size_t buf_size)
{
unsigned int i;
char *p = output_buffer;
memset(output_buffer, 0, 1 + (2*buf_size));
for (i = 0; i < buf_size ; i++)
p += sprintf(p, "%2.2x", buf[i]);
}
#ifdef THREADED_CHECKSUMS
static void *checksum_thread(void *arg)
{
struct algo_context *a = arg;
struct _checksum_context *c = a->parent;
int num_blocks_summed = 0;
while (1)
{
/* wait to be given some work to do */
pthread_mutex_lock(&a->start_mutex);
while (a->buf == NULL)
{
pthread_cond_wait(&a->start_cv, &a->start_mutex);
}
pthread_mutex_unlock(&a->start_mutex);
/* if we're given a zero-length buffer, then that means we're
* done */
if (a->len == 0)
break;
/* actually do the checksum on the supplied buffer */
algorithms[a->which].update(a->context, a->buf, a->len);
num_blocks_summed++;
a->buf = NULL;
/* and tell the main thread that we're done with that
* buffer */
pthread_mutex_lock(&c->done_mutex);
c->threads_running--;
if (c->threads_running == 0)
pthread_cond_signal(&c->done_cv);
pthread_mutex_unlock(&c->done_mutex);
}
pthread_exit(0);
}
#endif
checksum_context_t *checksum_init_context(int checksums, const char *owner)
{
int i = 0;
int ret = 0;
struct _checksum_context *context = calloc(1, sizeof(struct _checksum_context));
if (!context)
return NULL;
context->owner = strdup(owner);
if (!context->owner)
{
free(context);
return NULL;
}
#ifdef THREADED_CHECKSUMS
pthread_mutex_init(&context->done_mutex, NULL);
pthread_cond_init(&context->done_cv, NULL);
context->index = 0;
context->threads_running = 0;
context->threads_desired = 0;
for (i = 0; i < NUM_CHECKSUMS; i++)
if ( (1 << i) & checksums)
context->threads_desired++;
#endif
for (i = 0; i < NUM_CHECKSUMS; i++)
{
struct algo_context *a = &context->algo[i];
if ( (1 << i) & checksums)
{
a->context = malloc(algorithms[i].context_size);
if (!a->context)
{
checksum_free_context(context);
return NULL;
}
a->digest = malloc(algorithms[i].digest_size);
if (!a->digest)
{
checksum_free_context(context);
return NULL;
}
a->hexdump = malloc(1 + (2*algorithms[i].digest_size));
if (!a->hexdump)
{
checksum_free_context(context);
return NULL;
}
algorithms[i].init(a->context);
a->enabled = 1;
a->finalised = 0;
#ifdef THREADED_CHECKSUMS
a->which = i;
a->parent = context;
a->buf = NULL;
a->len = 0;
pthread_mutex_init(&a->start_mutex, NULL);
pthread_cond_init(&a->start_cv, NULL);
ret = pthread_create(&a->thread, NULL, checksum_thread, a);
if (ret != 0)
{
fprintf(stderr, "failed to create new thread: %d\n", ret);
checksum_free_context(context);
return NULL;
}
#endif
}
else
a->enabled = 0;
}
return context;
}
void checksum_free_context(checksum_context_t *context)
{
int i = 0;
struct _checksum_context *c = context;
for (i = 0; i < NUM_CHECKSUMS; i++)
{
struct algo_context *a = &c->algo[i];
#ifdef THREADED_CHECKSUMS
if (a->thread)
{
void *ret;
pthread_cancel(a->thread);
pthread_join(a->thread, &ret);
a->thread = 0;
}
#endif
free(a->context);
free(a->digest);
free(a->hexdump);
}
free(c->owner);
free(c);
}
#ifdef THREADED_CHECKSUMS
void checksum_update(checksum_context_t *context,
unsigned char const *buf, unsigned int len)
{
int i = 0;
struct _checksum_context *c = context;
static int index = 0;
index++;
c->threads_running = c->threads_desired;
for (i = 0; i < NUM_CHECKSUMS; i++)
{
if (c->algo[i].enabled)
{
struct algo_context *a = &c->algo[i];
pthread_mutex_lock(&a->start_mutex);
a->len = len;
a->buf = buf;
pthread_cond_signal(&a->start_cv);
pthread_mutex_unlock(&a->start_mutex);
}
}
/* Should now all be running, wait on them all to return */
pthread_mutex_lock(&c->done_mutex);
while (c->threads_running > 0)
{
pthread_cond_wait(&c->done_cv, &c->done_mutex);
}
pthread_mutex_unlock(&c->done_mutex);
}
#else // THREADED_CHECKSUMS
void checksum_update(checksum_context_t *context,
unsigned char const *buf, unsigned int len)
{
int i = 0;
struct _checksum_context *c = context;
for (i = 0; i < NUM_CHECKSUMS; i++)
{
if (c->algo[i].enabled)
{
struct algo_context *a = &c->algo[i];
algorithms[i].update(a->context, buf, len);
}
}
}
#endif // THREADED_CHECKSUMS
void checksum_final(checksum_context_t *context)
{
int i = 0;
struct _checksum_context *c = context;
#ifdef THREADED_CHECKSUMS
void *thread_ret;
/* Clean up the threads */
c->threads_running = c->threads_desired;
for (i = 0; i < NUM_CHECKSUMS; i++)
{
if (c->algo[i].enabled)
{
void *ret = 0;
struct algo_context *a = &c->algo[i];
pthread_mutex_lock(&a->start_mutex);
a->len = 0;
a->buf = (unsigned char *)-1;
pthread_cond_signal(&a->start_cv);
pthread_mutex_unlock(&a->start_mutex);
pthread_join(a->thread, &ret);
a->thread = 0;
}
}
#endif
for (i = 0; i < NUM_CHECKSUMS; i++)
{
struct algo_context *a = &c->algo[i];
if (a->enabled)
{
algorithms[i].final(a->digest, a->context);
hex_dump_to_buffer(a->hexdump, a->digest, algorithms[i].digest_size);
a->finalised = 1;
}
}
}
void checksum_copy(checksum_context_t *context,
enum checksum_types which,
unsigned char *digest)
{
struct _checksum_context *c = context;
if (c->algo[which].enabled)
{
if (c->algo[which].finalised)
memcpy(digest, c->algo[which].digest, algorithms[which].digest_size);
else
memset(digest, 0, algorithms[which].digest_size);
}
else
fprintf(stderr, "Asked for %s checksum, not enabled!\n",
algorithms[which].name);
}
const char *checksum_hex(checksum_context_t *context,
enum checksum_types which)
{
struct _checksum_context *c = context;
if (c->algo[which].enabled && c->algo[which].finalised)
return c->algo[which].hexdump;
/* else */
return NULL;
}
/* Parse the command line options for which checksums to use */
int parse_checksum_algo(char *arg, int *algo)
{
int error = 0;
int i = 0;
char *start_ptr = arg;
int len = 0;
*algo = 0;
if (!strcasecmp(arg, "all"))
{
*algo = 0xFF;
return 0;
}
while (*start_ptr != 0)
{
int match = 0;
len = 0;
while (start_ptr[len] != ',' && start_ptr[len] != 0)
len++;
if (len)
{
for (i = 0; i < NUM_CHECKSUMS; i++)
{
if (len == strlen(algorithms[i].name) &&
!strncasecmp(start_ptr, algorithms[i].name, len))
{
match = 1;
*algo |= (1 << i);
}
}
if (!match)
{
fprintf(stderr, "invalid algorithm name found in %s\n", arg);
return EINVAL;
}
}
if (start_ptr[len] == 0)
break;
start_ptr += len + 1;
}
if (! (*algo & CHECK_MD5_USED))
{
fprintf(stderr, "invalid choices: algorithms *must* include MD5\n");
return EINVAL;
}
return 0;
}
#ifdef CHECKSUM_SELF_TEST
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char buf[1024];
int fd = -1;
char *filename;
int err = 0;
static checksum_context_t *test_context = NULL;
int i = 0;
if (argc != 2)
{
fprintf(stderr, "Need a filename to act on!\n");
return 1;
}
filename = argv[1];
fd = open(filename, O_RDONLY);
if (fd < 0)
{
fprintf(stderr, "Unable to open file %s, errno %d\n", filename, errno);
return 1;
}
test_context = checksum_init_context(CHECK_ALL_USED, "test");
if (!test_context)
{
fprintf(stderr, "Unable to initialise checksum context\n");
return 1;
}
while(1)
{
err = read(fd, buf, sizeof(buf));
if (err < 0)
{
fprintf(stderr, "Failed to read from file, errno %d\n", errno);
return 1;
}
if (err == 0)
break; // EOF
/* else */
checksum_update(test_context, buf, err);
}
close(fd);
checksum_final(test_context);
for (i = 0; i < NUM_CHECKSUMS; i++)
{
struct checksum_info *info;
unsigned char r[64];
int j = 0;
info = checksum_information(i);
memset(r, 0, sizeof(r));
checksum_copy(test_context, i, r);
printf("OUR %s:\n", info->name);
for (j = 0; j < info->digest_size; j++)
printf("%2.2x", r[j]);
printf(" %s\n", filename);
printf("system checksum program (%s):\n", info->prog);
sprintf(buf, "%s %s", info->prog, filename);
system(buf);
printf("\n");
}
return 0;
}
#endif /* CHECKSUM_SELF_TEST */