/*- * Copyright (c) 2013,2015 Alistair Crooks * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #ifdef _KERNEL #include #include #else #include #include #include #include #include #include #include #endif /* digests */ #include "md5.h" #include "rmd160.h" #include "sha1.h" #include "sha2.h" #include "crc32c.h" #include "tiger.h" #include "blake2.h" #include "whirlpool.h" #include "keccak.h" #include "multigest.h" #ifndef __arraycount #define __arraycount(__x) (sizeof(__x) / sizeof(__x[0])) #endif #ifndef USE_ARG #define USE_ARG(x) /*LINTED*/(void)&(x) #endif #define MB(x) ((x) * 1024 * 1024) /* * If you'd like to add a digest to this library and/or utility, then * you need to write: * * 1. a wrapper function for the digest init function * 2. a wrapper function for the digest update function * 3. a wrapper function for the digest final function * * and then add the entry to the algs table below. * * The table-driven nature of this is not strictly necessary, but makes * it easier to add digest functions. * * The table is searched once at initialisation time, and so there is * only one table scan to find the correct digest algorithm. * Algorithms are searched in a case-insensitive manner. * The text for the algorithm name in the algs table is printed as the * name of the algorithm in the output. Convention has this as upper * case. */ /*****/ static void wrap_md5_init(void *v) { MD5Init(v); } static void wrap_md5_update(void *v, const void *data, unsigned len) { MD5Update(v, (const uint8_t *)data, len); } static void wrap_md5_final(uint8_t *raw, void *v) { MD5Final(raw, v); } /*****/ static void wrap_sha1_init(void *v) { SHA1Init(v); } static void wrap_sha1_update(void *v, const void *data, unsigned len) { SHA1Update(v, (const uint8_t *)data, len); } static void wrap_sha1_final(uint8_t *raw, void *v) { SHA1Final(raw, v); } /*****/ static void wrap_sha256_init(void *v) { SHA256_Init(v); } static void wrap_sha256_update(void *v, const void *data, unsigned len) { SHA256_Update(v, (const uint8_t *)data, len); } static void wrap_sha256_final(uint8_t *raw, void *v) { SHA256_Final(raw, v); } /*****/ static void wrap_sha512_init(void *v) { SHA512_Init(v); } static void wrap_sha512_update(void *v, const void *data, unsigned len) { SHA512_Update(v, (const uint8_t *)data, len); } static void wrap_sha512_final(uint8_t *raw, void *v) { SHA512_Final(raw, v); } /*****/ static void wrap_rmd160_init(void *v) { RMD160Init(v); } static void wrap_rmd160_update(void *v, const void *data, unsigned len) { RMD160Update(v, (const uint8_t *)data, len); } static void wrap_rmd160_final(uint8_t *raw, void *v) { RMD160Final(raw, v); } /*****/ static void wrap_crc32c_init(void *v) { crc32c_init(v); } static void wrap_crc32c_update(void *v, const void *data, unsigned len) { crc32c_update(v, (const uint8_t *)data, len); } static void wrap_crc32c_final(uint8_t *raw, void *v) { crc32c_final((ctx32_t *)(void *)raw, v); } /*****/ static void wrap_tiger_init(void *v) { TIGER_Init(v); } static void wrap_tiger2_init(void *v) { TIGER2_Init(v); } static void wrap_tiger_update(void *v, const void *data, unsigned len) { TIGER_Update(v, (const uint8_t *)data, len); } static void wrap_tiger_final(uint8_t *raw, void *v) { TIGER_Final(raw, v); } /*****/ static void wrap_blake2_init(void *v) { blake2b_init(v, 64); } static void wrap_blake2_update(void *v, const void *data, unsigned len) { blake2b_update(v, (const uint8_t *)data, (uint64_t)len); } static void wrap_blake2_final(uint8_t *raw, void *v) { blake2b_final(v, raw, 64); } /*****/ static void wrap_whirlpool_init(void *v) { whirlpool_init(v); } static void wrap_whirlpool_update(void *v, const void *data, unsigned len) { whirlpool_update(v, (const uint8_t *)data, len); } static void wrap_whirlpool_final(uint8_t *raw, void *v) { whirlpool_finalize((char *)raw, v); } /*****/ static void wrap_keccak224_init(void *v) { KECCAK_Init(v, 224); } static void wrap_keccak256_init(void *v) { KECCAK_Init(v, 256); } static void wrap_keccak384_init(void *v) { KECCAK_Init(v, 384); } static void wrap_keccak512_init(void *v) { KECCAK_Init(v, 512); } static void wrap_keccak_update(void *v, const void *data, unsigned len) { /* number of bits for keccak */ KECCAK_Update(v, (const uint8_t *)data, (uint64_t)(len * 8)); } static void wrap_keccak_final(uint8_t *raw, void *v) { KECCAK_Final(v, raw); } /*****/ static void wrap_size_init(void *v) { memset(v, 0x0, sizeof(uint64_t)); } static void wrap_size_update(void *v, const void *data, unsigned len) { uint64_t n; USE_ARG(data); memcpy(&n, v, sizeof(n)); n += len; memcpy(v, &n, sizeof(n)); } static void wrap_size_final(uint8_t *raw, void *v) { const int indian = 1; uint64_t tmp; uint64_t w; memcpy(&w, v, sizeof(w)); if (*(const char *)(const void *)&indian) { /* little endian */ tmp = (w >> 32) | (w << 32); \ tmp = ((tmp & (uint64_t)0xff00ff00ff00ff00ULL) >> 8) | ((tmp & (uint64_t)0x00ff00ff00ff00ffULL) << 8); w = ((tmp & (uint64_t)0xffff0000ffff0000ULL) >> 16) | ((tmp & (uint64_t)0x0000ffff0000ffffULL) << 16); } memcpy(raw, &w, sizeof(w)); } /*****/ static void wrap_null_init(void *v) { USE_ARG(v); } static void wrap_null_update(void *v, const void *data, unsigned len) { USE_ARG(v); USE_ARG(data); USE_ARG(len); } static void wrap_null_final(uint8_t *raw, void *v) { USE_ARG(raw); USE_ARG(v); } /*****/ #define COMBINE_CONCAT 0x0 #define COMBINE_COMB4P 0x1 #define COMBINE_HASH 0x2 #define COMBINE_XOR 0x3 /* digest algorithm struct */ typedef struct Alg { const char *name; /* digest name */ size_t namelen; /* length of name */ size_t ctxsize; /* context size */ size_t rawsize; /* rawsize of output */ mg_initfunc_t init; /* digest init function */ mg_updatefunc_t update; /* digest update function */ mg_finalfunc_t final; /* digest final function */ uint32_t combiner; /* combination type */ } Alg; static const Alg algs[] = { { "MD5", 3, sizeof(MD5_CTX), 16, wrap_md5_init, wrap_md5_update, wrap_md5_final, 0 }, { "SHA1", 4, sizeof(SHA1_CTX), 20, wrap_sha1_init, wrap_sha1_update, wrap_sha1_final, 0 }, { "SHA256", 6, sizeof(SHA256_CTX), 32, wrap_sha256_init, wrap_sha256_update, wrap_sha256_final, 0 }, { "SHA512", 6, sizeof(SHA512_CTX), 64, wrap_sha512_init, wrap_sha512_update, wrap_sha512_final, 0 }, { "BLAKE2", 6, sizeof(BLAKE2_CTX), 64, wrap_blake2_init, wrap_blake2_update, wrap_blake2_final, 0 }, { "RMD160", 6, sizeof(RMD160_CTX), 20, wrap_rmd160_init, wrap_rmd160_update, wrap_rmd160_final, 0 }, { "RIPEMD160", 9, sizeof(RMD160_CTX), 20, wrap_rmd160_init, wrap_rmd160_update, wrap_rmd160_final, 0 }, { "CRC32C", 6, sizeof(ctx32_t), 4, wrap_crc32c_init, wrap_crc32c_update, wrap_crc32c_final, 0 }, { "TIGER2", 6, sizeof(TIGER_CTX), 24, wrap_tiger2_init, wrap_tiger_update, wrap_tiger_final, 0 }, { "TIGER", 5, sizeof(TIGER_CTX), 24, wrap_tiger_init, wrap_tiger_update, wrap_tiger_final, 0 }, { "WHIRLPOOL", 9, sizeof(whirlpool_context_t), 64, wrap_whirlpool_init, wrap_whirlpool_update, wrap_whirlpool_final, 0 }, { "SHA3-224", 8, sizeof(KECCAK_CTX), 28, wrap_keccak224_init, wrap_keccak_update, wrap_keccak_final, 0 }, { "SHA3-256", 8, sizeof(KECCAK_CTX), 32, wrap_keccak256_init, wrap_keccak_update, wrap_keccak_final, 0 }, { "SHA3-384", 8, sizeof(KECCAK_CTX), 48, wrap_keccak384_init, wrap_keccak_update, wrap_keccak_final, 0 }, { "SHA3-512", 8, sizeof(KECCAK_CTX), 64, wrap_keccak512_init, wrap_keccak_update, wrap_keccak_final, 0 }, { "KECCAK-224", 10, sizeof(KECCAK_CTX), 28, wrap_keccak224_init, wrap_keccak_update, wrap_keccak_final, 0 }, { "KECCAK-256", 10, sizeof(KECCAK_CTX), 32, wrap_keccak256_init, wrap_keccak_update, wrap_keccak_final, 0 }, { "KECCAK-384", 10, sizeof(KECCAK_CTX), 48, wrap_keccak384_init, wrap_keccak_update, wrap_keccak_final, 0 }, { "KECCAK-512", 10, sizeof(KECCAK_CTX), 64, wrap_keccak512_init, wrap_keccak_update, wrap_keccak_final, 0 }, { "SIZE", 4, sizeof(uint64_t), 8, wrap_size_init, wrap_size_update, wrap_size_final, 0 }, { "COMBCONCAT", 10, 0, 0, wrap_null_init, wrap_null_update, wrap_null_final, COMBINE_CONCAT }, { "CONCAT", 6, 0, 0, wrap_null_init, wrap_null_update, wrap_null_final, COMBINE_CONCAT }, { "COMB4P", 6, 0, 0, wrap_null_init, wrap_null_update, wrap_null_final, COMBINE_COMB4P }, { "COMBHASH", 8, 0, 0, wrap_null_init, wrap_null_update, wrap_null_final, COMBINE_HASH }, { "HASH", 4, 0, 0, wrap_null_init, wrap_null_update, wrap_null_final, COMBINE_HASH }, { "COMBXOR", 7, 0, 0, wrap_null_init, wrap_null_update, wrap_null_final, COMBINE_XOR }, { "XOR", 3, 0, 0, wrap_null_init, wrap_null_update, wrap_null_final, COMBINE_XOR }, { NULL, 0, 0, 0, NULL, NULL, NULL, 0} }; /* find an algorithm in the table above */ static const Alg * findalg(const char *algname) { const Alg *alg; for (alg = algs ; algname && alg->name ; alg++) { if (strncasecmp(algname, alg->name, alg->namelen) == 0) { return alg; } } return NULL; } /* normalise through regexp substitution */ static int normalise(multigest_t *multigest, const char *data, size_t len, int64_t *from) { #ifndef _KERNEL multigest_dig_t *d; regmatch_t match[2]; uint32_t i; *from = 0; while (multigest->r && len > 0) { match[0].rm_so = *from; match[0].rm_eo = (regoff_t)len; if (regexec(multigest->r, data, 2, match, REG_STARTEND) != 0) { break; } for (d = multigest->digs, i = 0 ; i < multigest->digc ; i++, d++) { if (d->rawsize) { (*d->update)(&multigest->ctx[d->ctxoff], &data[*from], (unsigned)(match[0].rm_so - *from)); if (multigest->repllen) { (*d->update)(&multigest->ctx[d->ctxoff], multigest->repl, multigest->repllen); } } } *from = match[0].rm_eo; } #else *from = 0; #endif return 1; } /* xor the contents of two buffers together */ static void xorbuf(uint8_t *out, uint8_t *in1, uint8_t *in2, size_t size) { uint32_t j; for (j = 0 ; j < size ; j++) { out[j] = in1[j] ^ in2[j]; } } /* a round of comb4p combination */ static int comb4p_round(multigest_t *m, uint8_t *out, uint8_t *in, multigest_dig_t *d1, multigest_dig_t *d2, uint32_t r) { const int indian = 1; uint32_t b2; uint32_t b4; uint8_t h1[4096]; uint8_t h2[4096]; if (*(const char *)(const void *)&indian) { /* little endian - convert to bg endian) */ b2 = (r & 0x00ff0000); b4 = (r & 0x000000ff); r = (r & 0xff00ff00) | (b2 >> 16) | (b4 << 16); } (*d1->update)(&m->ctx[d1->ctxoff], (const char *)&r, sizeof(r)); (*d2->update)(&m->ctx[d2->ctxoff], (const char *)&r, sizeof(r)); (*d1->update)(&m->ctx[d1->ctxoff], (const char *)in, (unsigned)d1->rawsize); (*d2->update)(&m->ctx[d2->ctxoff], (const char *)in, (unsigned)d2->rawsize); (*d1->final)(h1, &m->ctx[d1->ctxoff]); xorbuf(out, out, h1, d1->rawsize); (*d2->final)(h2, &m->ctx[d2->ctxoff]); xorbuf(out, out, h2, d2->rawsize); return 1; } /* point d1 and d2 at the first 2 digests found */ static int find_digests(multigest_t *m, multigest_dig_t **d1, multigest_dig_t **d2) { multigest_dig_t *d; uint32_t i; *d1 = *d2 = NULL; for (d = m->digs, i = 0 ; i < m->digc ; i++, d++) { if (d->rawsize) { if (*d1) { *d2 = d; return 1; } *d1 = d; } } return 0; } /***************************************************************************/ /* create a new struct and return it */ multigest_t * multigest_new(void) { return calloc(1, sizeof(multigest_t)); } /* initialise a struct */ int multigest_init(multigest_t *multigest, const char *algname) { multigest_dig_t *d; multigest_dig_t *d1; multigest_dig_t *d2; const Alg *alg; uint32_t ctxoff; uint32_t i; uint8_t *newv; if (multigest && algname) { memset(multigest, 0x0, sizeof(*multigest)); multigest->type = strdup(algname); for (i = 0, d = multigest->digs, ctxoff = 0 ; *algname ; d++, i++) { if (i >= __arraycount(multigest->digs)) { fprintf(stderr, "too many digest types %u\n", i); break; } if ((alg = findalg(algname)) == NULL) { fprintf(stderr, "no such algorithm '%.10s'\n", algname); break; } if (alg->combiner) { multigest->combiner = alg->combiner; } if (ctxoff + alg->ctxsize >= multigest->ctxsize) { if ((newv = realloc(multigest->ctx, multigest->ctxsize + 4096)) == NULL) { fprintf(stderr, "multigest_init: allocation issues\n"); return 0; } multigest->ctx = newv; multigest->ctxsize += 4096; } d->alg = strdup(alg->name); (*alg->init)(&multigest->ctx[ctxoff]); d->rawsize = alg->rawsize; multigest->rawsize += alg->rawsize; d->ctxoff = ctxoff; d->update = alg->update; d->final = alg->final; ctxoff += (uint32_t)alg->ctxsize; algname += alg->namelen; if (*algname == ',') { algname += 1; } multigest->digc += 1; } switch (multigest->combiner) { case COMBINE_CONCAT: multigest->outsize = multigest->rawsize; break; case COMBINE_COMB4P: if (!find_digests(multigest, &d1, &d2)) { fprintf(stderr, "multigest: comb4p < 2 digests\n"); return 0; } multigest->outsize = d1->rawsize * 2; break; case COMBINE_XOR: if (!find_digests(multigest, &d1, &d2)) { fprintf(stderr, "multigest: xor < 2 digests\n"); return 0; } multigest->outsize = d1->rawsize; break; case COMBINE_HASH: if (!find_digests(multigest, &d1, &d2)) { fprintf(stderr, "multigest: hash < 2 digests\n"); return 0; } multigest->outsize = d1->rawsize; break; } return 1; } fprintf(stderr, "!multigest || !algname\n"); return 0; } /* add a substitution pattern */ int multigest_add_subst(multigest_t *multigest, const char *from, const char *to) { if (multigest && from && from[0]) { if ((multigest->r = calloc(1, sizeof(regex_t))) == NULL || regcomp(multigest->r, from, REG_EXTENDED) != 0) { return 0; } multigest->pat = strdup(from); if (to) { multigest->repl = strdup(to); multigest->repllen = (uint32_t)strlen(to); } return 1; } return 0; } /* update the digest with the input */ void multigest_update(multigest_t *multigest, const void *vdata, size_t len) { multigest_dig_t *d; const char *data = (const char *)vdata; uint32_t i; int64_t from; if (multigest && data) { normalise(multigest, data, len, &from); for (d = multigest->digs, i = 0 ; i < multigest->digc ; i++, d++) { if (d->rawsize) { (*d->update)(&multigest->ctx[d->ctxoff], &data[from], (unsigned)(len - (unsigned)from)); } } } } /* finalise the digest */ void multigest_final(multigest_t *m, uint8_t *raw) { multigest_dig_t *d1; multigest_dig_t *d2; uint32_t rawoff; uint32_t i; uint8_t h1[4096]; uint8_t h2[4096]; if (m && raw) { switch(m->combiner) { case COMBINE_COMB4P: if (!find_digests(m, &d1, &d2)) { return; } memset(h1, 0x0, sizeof(h1)); memset(h2, 0x0, sizeof(h2)); (*d1->final)(h1, &m->ctx[d1->ctxoff]); (*d2->final)(h2, &m->ctx[d2->ctxoff]); xorbuf(h1, h1, h2, d2->rawsize); comb4p_round(m, h2, h1, d1, d2, 1); comb4p_round(m, h1, h2, d1, d2, 2); memcpy(raw, h1, d1->rawsize); memcpy(&raw[d1->rawsize], h2, d2->rawsize); break; case COMBINE_CONCAT: rawoff = 0; for (d1 = m->digs, i = 0 ; i < m->digc ; i++, d1++) { if (d1->rawsize) { (*d1->final)(&raw[rawoff], &m->ctx[d1->ctxoff]); rawoff += (uint32_t)d1->rawsize; } } break; case COMBINE_HASH: if (!find_digests(m, &d1, &d2)) { return; } (*d2->final)(h2, &m->ctx[d2->ctxoff]); (*d1->update)(&m->ctx[d1->ctxoff], h2, (unsigned)d1->rawsize); (*d1->final)(raw, &m->ctx[d1->ctxoff]); break; case COMBINE_XOR: if (!find_digests(m, &d1, &d2)) { return; } (*d2->final)(h2, &m->ctx[d2->ctxoff]); (*d1->final)(h1, &m->ctx[d1->ctxoff]); xorbuf(raw, h1, h2, m->outsize); break; default: break; } } } /* run sed on data and then digest it */ uint8_t * multigest_data(const char *alg, const void *data, size_t size, uint8_t *raw, const char *pat, const char *repl) { multigest_t m; if (data && alg && raw) { memset(&m, 0x0, sizeof(m)); multigest_init(&m, alg); multigest_add_subst(&m, pat, repl); multigest_update(&m, data, size); multigest_final(&m, raw); multigest_free(&m); return raw; } return NULL; } /* percent decode (pseudo-RFC1738) a string */ void multigest_unpcstring(const char *in, size_t isize, char *out, size_t osize) { static const char *hexes = "0123456789abcdef"; const char *p[2]; const char *i; uint8_t num; char *o; for (i = in, o = out ; (size_t)(o - out) < osize - 1 && (size_t)(i - in) < isize && *i ; o++) { if (*i == '%') { if ((p[0] = strchr(hexes, i[1])) == NULL || (p[1] = strchr(hexes, i[2])) == NULL) { break; } num = (uint8_t)((p[0] - hexes) << 4) | (uint8_t)(p[1] - hexes); *o = (char)num; i += 3; } else { *o = *i++; } } *o = 0x0; } /* print as hex string */ int multigest_format_hex(uint8_t *raw, const char *algname, char *out, size_t size) { const Alg *alg; size_t rawsize; size_t i; for (rawsize = 0 ; *algname ; rawsize += alg->rawsize) { if ((alg = findalg(algname)) == NULL) { break; } for (i = 0 ; i < alg->rawsize && (rawsize + i) * 2 < size; i++) { snprintf(&out[(rawsize + i) * 2], 3, "%02hhx", raw[rawsize + i]); } algname += alg->namelen; if (*algname == ',') { algname += 1; } } return (int)(rawsize + rawsize); } /* return the size of output array we'll need */ uint32_t multigest_get_rawsize(multigest_t *multigest) { return (multigest) ? (uint32_t)multigest->outsize : 0; } /* return the size of output array we'll need for the alg names */ uint32_t multigest_algs_rawsize(const char *alg) { multigest_t m; uint32_t size; memset(&m, 0x0, sizeof(m)); if (!multigest_init(&m, alg)) { fprintf(stderr, "multigest_init: failed\n"); return 0; } size = multigest_get_rawsize(&m); multigest_free(&m); return size; } /*****************************************************************/ #ifndef _KERNEL /* percent encode (pseudo-RFC1738) a string */ static void pcstring(FILE *fp, const char *s) { static const char *pcencodes = "%$\r\n\t "; for ( ; *s ; s++) { if (strchr(pcencodes, *s) == NULL) { fprintf(fp, "%c", *s); } else { fprintf(fp, "%%%02hhx", *s); } } } /* print as hex string */ int multigest_print_hex(uint8_t *raw, const char *algname, const char *outname, const char *f, const char *sub, const char *sep, const char *format) { const Alg *alg; size_t outsize; size_t rawsize; size_t i; FILE *fp; if (outname == NULL) { fp = stdout; } else { if ((fp = fopen(outname, "w")) == NULL) { fprintf(stderr, "can't write to '%s'\n", outname); return 0; } } if (f != NULL) { for (i = 0 ; algname[i] ; i++) { fprintf(fp, "%c", toupper((uint8_t)algname[i])); } if (format && strcasecmp(format, "openssl") == 0) { fprintf(fp, "(%s)= ", f); } else if (format && strcasecmp(format, "digest") == 0) { fprintf(fp, " (%s) = ", f); } else { fprintf(fp, " (%s) (", f); if (sub) { pcstring(fp, sub); } fprintf(fp, ") = "); } } outsize = multigest_algs_rawsize(algname); for (rawsize = 0 ; *algname && rawsize < outsize ; rawsize += alg->rawsize) { if ((alg = findalg(algname)) == NULL) { break; } if (!alg->combiner) { for (i = 0 ; i < alg->rawsize ; i++) { fprintf(fp, "%02hhx", raw[rawsize + i]); } if (sep) { fprintf(fp, "%s", sep); } } algname += alg->namelen; if (*algname == ',') { algname += 1; } } fprintf(fp, "\n"); if (outname != NULL) { fclose(fp); } return 1; } /* run sed, then digest on a file */ uint8_t * multigest_file(const char *alg, const char *f, uint8_t *raw, const char *pat, const char *repl) { struct stat st; multigest_t m; ssize_t rc; size_t size; size_t cc; char *mapped; FILE *fp; if (f && alg && raw) { memset(&m, 0x0, sizeof(m)); multigest_init(&m, alg); multigest_add_subst(&m, pat, repl); if ((fp = fopen(f, "r")) == NULL) { fprintf(stderr, "can't open '%s'\n", f); return 0; } fstat(fileno(fp), &st); size = (size_t)st.st_size; mapped = mmap(NULL, size, PROT_READ, MAP_SHARED, fileno(fp), 0); if (mapped == MAP_FAILED) { mapped = calloc(1, MB(1)); for (cc = 0 ; cc < size ; cc += (size_t)rc) { if ((rc = read(fileno(fp), mapped, MB(1))) <= 0) { break; } multigest_update(&m, mapped, (size_t)rc); } free(mapped); } else { multigest_update(&m, mapped, (size_t)size); munmap(mapped, size); } fclose(fp); multigest_final(&m, raw); multigest_free(&m); return raw; } return NULL; } /* free resources used in a sedded digest */ void multigest_free(multigest_t *s) { uint32_t i; if (s) { if (s->ctx) { free(s->ctx); } if (s->pat) { free(s->pat); regfree(s->r); } if (s->repl) { free(s->repl); } for (i = 0 ; i < s->digc ; i++) { free(s->digs[i].alg); } } } /* turn a raw digest into an ascii string */ char * multigest_format_raw(const uint8_t *in, size_t insize, char *out, size_t outsize) { const uint8_t *i; char *o; for (o = out, i = in ; (size_t)(i - in) < insize && (size_t)(o - out) < outsize ; i++, o += 2) { snprintf(o, 3, "%02hhx", *i); } *o = 0x0; return out; } #endif