4 * Copyright (c) 2008-2019 Steve McIntyre <steve@einval.com>
6 * Implementation of a generic checksum interface, used in JTE.
12 #include "../config.h"
15 #include <sys/types.h>
25 #ifdef HAVE_INTTYPES_H
36 #ifdef THREADED_CHECKSUMS
40 static void md5_init(void *context)
44 static void md5_update(void *context, unsigned char const *buf, unsigned int len)
46 mk_MD5Update(context, buf, len);
48 static void md5_final(unsigned char *digest, void *context)
50 mk_MD5Final(digest, context);
53 static void sha1_init(void *context)
55 sha1_init_ctx(context);
57 static void sha1_update(void *context, unsigned char const *buf, unsigned int len)
59 sha1_write(context, buf, len);
61 static void sha1_final(unsigned char *digest, void *context)
63 sha1_finish_ctx(context);
64 memcpy(digest, sha1_read(context), 20);
67 static void sha256_init(void *context)
69 sha256_init_ctx(context);
71 static void sha256_update(void *context, unsigned char const *buf, unsigned int len)
73 sha256_process_bytes(buf, len, context);
75 static void sha256_final(unsigned char *digest, void *context)
77 sha256_finish_ctx(context, digest);
80 static void sha512_init(void *context)
82 sha512_init_ctx(context);
84 static void sha512_update(void *context, unsigned char const *buf, unsigned int len)
86 sha512_process_bytes(buf, len, context);
88 static void sha512_final(unsigned char *digest, void *context)
90 sha512_finish_ctx(context, digest);
93 struct checksum_details
99 void (*init)(void *context);
100 void (*update)(void *context, unsigned char const *buf, unsigned int len);
101 void (*final)(unsigned char *digest, void *context);
102 int check_used_value;
105 static const struct checksum_details algorithms[] =
111 sizeof(struct mk_MD5Context),
121 sizeof(SHA1_CONTEXT),
131 sizeof(struct sha256_ctx),
141 sizeof(struct sha512_ctx),
152 unsigned char *digest;
156 #ifdef THREADED_CHECKSUMS
157 unsigned char const *buf;
161 struct _checksum_context *parent;
162 pthread_mutex_t start_mutex;
163 pthread_cond_t start_cv;
167 struct _checksum_context
169 #ifdef THREADED_CHECKSUMS
171 unsigned int threads_running;
172 unsigned int threads_desired;
173 pthread_mutex_t done_mutex;
174 pthread_cond_t done_cv;
177 struct algo_context algo[NUM_CHECKSUMS];
180 struct checksum_info *checksum_information(enum checksum_types which)
182 return (struct checksum_info *)&algorithms[which];
185 /* Dump a buffer in hex */
186 static void hex_dump_to_buffer(char *output_buffer, unsigned char *buf, size_t buf_size)
189 char *p = output_buffer;
191 memset(output_buffer, 0, 1 + (2*buf_size));
192 for (i = 0; i < buf_size ; i++)
193 p += sprintf(p, "%2.2x", buf[i]);
196 #ifdef THREADED_CHECKSUMS
197 static void *checksum_thread(void *arg)
199 struct algo_context *a = arg;
200 struct _checksum_context *c = a->parent;
201 int num_blocks_summed = 0;
205 /* wait to be given some work to do */
206 pthread_mutex_lock(&a->start_mutex);
207 while (a->buf == NULL)
209 pthread_cond_wait(&a->start_cv, &a->start_mutex);
211 pthread_mutex_unlock(&a->start_mutex);
213 /* if we're given a zero-length buffer, then that means we're
218 /* actually do the checksum on the supplied buffer */
219 algorithms[a->which].update(a->context, a->buf, a->len);
223 /* and tell the main thread that we're done with that
225 pthread_mutex_lock(&c->done_mutex);
226 c->threads_running--;
227 if (c->threads_running == 0)
228 pthread_cond_signal(&c->done_cv);
229 pthread_mutex_unlock(&c->done_mutex);
236 checksum_context_t *checksum_init_context(int checksums, const char *owner)
239 #ifdef THREADED_CHECKSUMS
243 struct _checksum_context *context = calloc(1, sizeof(struct _checksum_context));
248 context->owner = strdup(owner);
255 #ifdef THREADED_CHECKSUMS
256 pthread_mutex_init(&context->done_mutex, NULL);
257 pthread_cond_init(&context->done_cv, NULL);
259 context->threads_running = 0;
260 context->threads_desired = 0;
262 for (i = 0; i < NUM_CHECKSUMS; i++)
263 if ( (1 << i) & checksums)
264 context->threads_desired++;
267 for (i = 0; i < NUM_CHECKSUMS; i++)
269 struct algo_context *a = &context->algo[i];
270 if ( (1 << i) & checksums)
272 a->context = malloc(algorithms[i].context_size);
275 checksum_free_context(context);
278 a->digest = malloc(algorithms[i].digest_size);
281 checksum_free_context(context);
284 a->hexdump = malloc(1 + (2*algorithms[i].digest_size));
287 checksum_free_context(context);
290 algorithms[i].init(a->context);
293 #ifdef THREADED_CHECKSUMS
298 pthread_mutex_init(&a->start_mutex, NULL);
299 pthread_cond_init(&a->start_cv, NULL);
300 ret = pthread_create(&a->thread, NULL, checksum_thread, a);
303 /* libjte issues an own message:
304 fprintf(stderr, "failed to create new thread: %d\n", ret);
306 checksum_free_context(context);
318 void checksum_free_context(checksum_context_t *context)
321 struct _checksum_context *c = context;
323 for (i = 0; i < NUM_CHECKSUMS; i++)
325 struct algo_context *a = &c->algo[i];
327 #ifdef THREADED_CHECKSUMS
331 pthread_cancel(a->thread);
332 pthread_join(a->thread, &ret);
344 #ifdef THREADED_CHECKSUMS
345 void checksum_update(checksum_context_t *context,
346 unsigned char const *buf, unsigned int len)
349 struct _checksum_context *c = context;
351 /* >>> TODO : Find out for what purpose index shall serve.
352 It is defined here and incremented. Not more.
354 static int index = 0;
358 c->threads_running = c->threads_desired;
359 for (i = 0; i < NUM_CHECKSUMS; i++)
361 if (c->algo[i].enabled)
363 struct algo_context *a = &c->algo[i];
364 pthread_mutex_lock(&a->start_mutex);
367 pthread_cond_signal(&a->start_cv);
368 pthread_mutex_unlock(&a->start_mutex);
372 /* Should now all be running, wait on them all to return */
373 pthread_mutex_lock(&c->done_mutex);
374 while (c->threads_running > 0)
376 pthread_cond_wait(&c->done_cv, &c->done_mutex);
378 pthread_mutex_unlock(&c->done_mutex);
381 #else /* THREADED_CHECKSUMS */
383 void checksum_update(checksum_context_t *context,
384 unsigned char const *buf, unsigned int len)
387 struct _checksum_context *c = context;
389 for (i = 0; i < NUM_CHECKSUMS; i++)
391 if (c->algo[i].enabled)
393 struct algo_context *a = &c->algo[i];
394 algorithms[i].update(a->context, buf, len);
399 #endif /* THREADED_CHECKSUMS */
401 void checksum_final(checksum_context_t *context)
404 struct _checksum_context *c = context;
406 #ifdef THREADED_CHECKSUMS
407 /* Clean up the threads */
408 c->threads_running = c->threads_desired;
410 for (i = 0; i < NUM_CHECKSUMS; i++)
412 if (c->algo[i].enabled)
415 struct algo_context *a = &c->algo[i];
417 pthread_mutex_lock(&a->start_mutex);
419 a->buf = (unsigned char *)-1;
420 pthread_cond_signal(&a->start_cv);
421 pthread_mutex_unlock(&a->start_mutex);
422 pthread_join(a->thread, &ret);
428 for (i = 0; i < NUM_CHECKSUMS; i++)
430 struct algo_context *a = &c->algo[i];
433 algorithms[i].final(a->digest, a->context);
434 hex_dump_to_buffer(a->hexdump, a->digest, algorithms[i].digest_size);
440 void checksum_copy(checksum_context_t *context,
441 enum checksum_types which,
442 unsigned char *digest)
444 struct _checksum_context *c = context;
446 if (c->algo[which].enabled)
448 if (c->algo[which].finalised)
449 memcpy(digest, c->algo[which].digest, algorithms[which].digest_size);
451 memset(digest, 0, algorithms[which].digest_size);
455 /* >>> TODO : ??? Can this happen ? Why print and then go on ? */
456 fprintf(stderr, "Asked for %s checksum, not enabled!\n",
457 algorithms[which].name);
460 const char *checksum_hex(checksum_context_t *context,
461 enum checksum_types which)
463 struct _checksum_context *c = context;
465 if (c->algo[which].enabled && c->algo[which].finalised)
466 return c->algo[which].hexdump;
473 /* Parse the command line options for which checksums to use */
474 int parse_checksum_algo(char *arg, int *algo)
477 char *start_ptr = arg;
480 (*algo) |= CHECK_MD5_USED;
482 if (!strcasecmp(arg, "all"))
488 while (*start_ptr != 0)
493 while (start_ptr[len] != ',' && start_ptr[len] != 0)
498 for (i = 0; i < NUM_CHECKSUMS; i++)
500 if (len == strlen(algorithms[i].name) &&
501 !strncasecmp(start_ptr, algorithms[i].name, len))
504 *algo |= algorithms[i].check_used_value;
514 if (start_ptr[len] == 0)
517 start_ptr += len + 1;
523 /* Helper function: Simply calculate the checksum of the first "size"
524 * bytes of a file using the specified algorithm. If size == -1,
525 * calculate the checksum for the whole of the file. The caller is
526 * responsible for passing in a large enough buffer as "digest", based
527 * on their choice of algorithm. */
528 int checksum_calculate(char *filename,
531 enum checksum_types which)
537 struct checksum_context_t *context;
539 context = checksum_init_context(1 << which, "misc");
546 infile = fopen(filename, "rb");
560 use = (remain > sizeof(buffer) ? sizeof(buffer) : remain);
561 if (fread(buffer, 1, use, infile) == 0)
563 /* Update the checksum */
564 checksum_update(context, (unsigned char *)buffer, use);
568 checksum_final(context);
569 checksum_copy(context, which, out);
570 checksum_free_context(context);
574 /* Read in a hex-dumped checksum and parse it */
575 int checksum_parse_hex(char *in, unsigned char *out, int size)
579 if (size % 2) /* odd number */
582 for (i = 0; i < size / 2; i++)
584 if (in[2*i] >= '0' && in[2*i] <= '9')
586 else if (in[2*i] >= 'A' && in[2*i] <= 'F')
588 else if (in[2*i] >= 'a' && in[2*i] <= 'f')
592 if (in[1+(2*i)] >= '0' && in[1+(2*i)] <= '9')
594 else if (in[1+(2*i)] >= 'A' && in[1+(2*i)] <= 'F')
595 in[1+(2*i)] += 10 - 'A';
596 else if (in[1+(2*i)] >= 'a' && in[1+(2*i)] <= 'f')
597 in[1+(2*i)] += 10 - 'a';
600 out[i] = in[2*i] << 4 | in[1+(2*i)];
605 #ifdef CHECKSUM_SELF_TEST
606 #include <sys/types.h>
607 #include <sys/stat.h>
613 int main(int argc, char **argv)
619 static checksum_context_t *test_context = NULL;
624 fprintf(stderr, "Need a filename to act on!\n");
629 fd = open(filename, O_RDONLY);
632 fprintf(stderr, "Unable to open file %s, errno %d\n", filename, errno);
636 test_context = checksum_init_context(CHECK_ALL_USED, "test");
639 fprintf(stderr, "Unable to initialise checksum context\n");
646 err = read(fd, buf, sizeof(buf));
649 fprintf(stderr, "Failed to read from file, errno %d\n", errno);
657 checksum_update(test_context, buf, err);
660 checksum_final(test_context);
662 for (i = 0; i < NUM_CHECKSUMS; i++)
664 struct checksum_info *info;
668 info = checksum_information(i);
669 memset(r, 0, sizeof(r));
671 checksum_copy(test_context, i, r);
673 printf("OUR %s:\n", info->name);
674 for (j = 0; j < info->digest_size; j++)
675 printf("%2.2x", r[j]);
676 printf(" %s\n", filename);
677 printf("system checksum program (%s):\n", info->prog);
678 sprintf(buf, "%s %s", info->prog, filename);
684 #endif /* CHECKSUM_SELF_TEST */