diff options
Diffstat (limited to 'rpmio/digest.c')
-rw-r--r-- | rpmio/digest.c | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/rpmio/digest.c b/rpmio/digest.c new file mode 100644 index 0000000..66f27b5 --- /dev/null +++ b/rpmio/digest.c @@ -0,0 +1,246 @@ +/** \ingroup signature + * \file rpmio/digest.c + */ + +#include "system.h" + +#include "rpmio/digest.h" + +#include "debug.h" + +#ifdef SHA_DEBUG +#define DPRINTF(_a) fprintf _a +#else +#define DPRINTF(_a) +#endif + + +/** + * MD5/SHA1 digest private data. + */ +struct DIGEST_CTX_s { + rpmDigestFlags flags; /*!< Bit(s) to control digest operation. */ + HASHContext *hashctx; /*!< Internal NSS hash context. */ + int algo; /*!< Used hash algorithm */ +}; + +#define DIGESTS_MAX 11 +struct rpmDigestBundle_s { + int index_min; /*!< Smallest index of active digest */ + int index_max; /*!< Largest index of active digest */ + off_t nbytes; /*!< Length of total input data */ + DIGEST_CTX digests[DIGESTS_MAX]; /*!< Digest contexts indexed by algo */ +}; + +rpmDigestBundle rpmDigestBundleNew(void) +{ + rpmDigestBundle bundle = xcalloc(1, sizeof(*bundle)); + return bundle; +} + +rpmDigestBundle rpmDigestBundleFree(rpmDigestBundle bundle) +{ + if (bundle) { + for (int i = bundle->index_min; i <= bundle->index_max ; i++) { + if (bundle->digests[i] == NULL) + continue; + rpmDigestFinal(bundle->digests[i], NULL, NULL, 0); + bundle->digests[i] = NULL; + } + memset(bundle, 0, sizeof(*bundle)); + free(bundle); + } + return NULL; +} + +int rpmDigestBundleAdd(rpmDigestBundle bundle, int algo, + rpmDigestFlags flags) +{ + DIGEST_CTX ctx = NULL; + if (bundle && algo > 0 && algo < DIGESTS_MAX) { + if (bundle->digests[algo] == NULL) { + ctx = rpmDigestInit(algo, flags); + if (ctx) { + bundle->digests[algo] = ctx; + if (algo < bundle->index_min) { + bundle->index_min = algo; + } + if (algo > bundle->index_max) { + bundle->index_max = algo; + } + } + } + } + return (ctx != NULL); +} + +int rpmDigestBundleUpdate(rpmDigestBundle bundle, const void *data, size_t len) +{ + int rc = 0; + if (bundle && data && len > 0) { + for (int i = bundle->index_min; i <= bundle->index_max; i++) { + DIGEST_CTX ctx = bundle->digests[i]; + if (ctx == NULL) + continue; + rc += rpmDigestUpdate(ctx, data, len); + } + bundle->nbytes += len; + } + return rc; +} + +int rpmDigestBundleFinal(rpmDigestBundle bundle, + int algo, void ** datap, size_t * lenp, int asAscii) +{ + int rc = 0; + if (bundle && algo >= bundle->index_min && algo <= bundle->index_max) { + rc = rpmDigestFinal(bundle->digests[algo], datap, lenp, asAscii); + bundle->digests[algo] = NULL; + } + return rc; +} + +DIGEST_CTX rpmDigestBundleDupCtx(rpmDigestBundle bundle, int algo) +{ + DIGEST_CTX dup = NULL; + if (bundle && algo >= bundle->index_min && algo <= bundle->index_max) { + dup = rpmDigestDup(bundle->digests[algo]); + } + return dup; +} + +DIGEST_CTX +rpmDigestDup(DIGEST_CTX octx) +{ + DIGEST_CTX nctx = NULL; + if (octx) { + HASHContext *hctx = HASH_Clone(octx->hashctx); + if (hctx) { + nctx = memcpy(xcalloc(1, sizeof(*nctx)), octx, sizeof(*nctx)); + nctx->hashctx = hctx; + } + } + return nctx; +} + +RPM_GNUC_PURE +static HASH_HashType getHashType(int hashalgo) +{ + switch (hashalgo) { + case PGPHASHALGO_MD5: + return HASH_AlgMD5; + break; + case PGPHASHALGO_MD2: + return HASH_AlgMD2; + break; + case PGPHASHALGO_SHA1: + return HASH_AlgSHA1; + break; + case PGPHASHALGO_SHA256: + return HASH_AlgSHA256; + break; + case PGPHASHALGO_SHA384: + return HASH_AlgSHA384; + break; + case PGPHASHALGO_SHA512: + return HASH_AlgSHA512; + break; + case PGPHASHALGO_RIPEMD160: + case PGPHASHALGO_TIGER192: + case PGPHASHALGO_HAVAL_5_160: + default: + return HASH_AlgNULL; + break; + } +} + +size_t +rpmDigestLength(int hashalgo) +{ + return HASH_ResultLen(getHashType(hashalgo)); +} + +DIGEST_CTX +rpmDigestInit(int hashalgo, rpmDigestFlags flags) +{ + HASH_HashType type = getHashType(hashalgo); + HASHContext *hashctx = NULL; + DIGEST_CTX ctx = NULL; + + if (type == HASH_AlgNULL || rpmInitCrypto() < 0) + goto exit; + + if ((hashctx = HASH_Create(type)) != NULL) { + ctx = xcalloc(1, sizeof(*ctx)); + ctx->flags = flags; + ctx->algo = hashalgo; + ctx->hashctx = hashctx; + HASH_Begin(ctx->hashctx); + } + +DPRINTF((stderr, "*** Init(%x) ctx %p hashctx %p\n", flags, ctx, ctx->hashctx)); +exit: + return ctx; +} + +int +rpmDigestUpdate(DIGEST_CTX ctx, const void * data, size_t len) +{ + size_t partlen; + const unsigned char *ptr = data; + + if (ctx == NULL) + return -1; + +DPRINTF((stderr, "*** Update(%p,%p,%zd) hashctx %p \"%s\"\n", ctx, data, len, ctx->hashctx, ((char *)data))); + partlen = ~(unsigned int)0xFF; + while (len > 0) { + if (len < partlen) { + partlen = len; + } + HASH_Update(ctx->hashctx, ptr, partlen); + ptr += partlen; + len -= partlen; + } + return 0; +} + +int +rpmDigestFinal(DIGEST_CTX ctx, void ** datap, size_t *lenp, int asAscii) +{ + unsigned char * digest; + unsigned int digestlen; + + if (ctx == NULL) + return -1; + digestlen = HASH_ResultLenContext(ctx->hashctx); + digest = xmalloc(digestlen); + +DPRINTF((stderr, "*** Final(%p,%p,%p,%zd) hashctx %p digest %p\n", ctx, datap, lenp, asAscii, ctx->hashctx, digest)); +/* FIX: check rc */ + HASH_End(ctx->hashctx, digest, (unsigned int *) &digestlen, digestlen); + + /* Return final digest. */ + if (!asAscii) { + if (lenp) *lenp = digestlen; + if (datap) { + *datap = digest; + digest = NULL; + } + } else { + if (lenp) *lenp = (2*digestlen) + 1; + if (datap) { + const uint8_t * s = (const uint8_t *) digest; + *datap = pgpHexStr(s, digestlen); + } + } + if (digest) { + memset(digest, 0, digestlen); /* In case it's sensitive */ + free(digest); + } + HASH_Destroy(ctx->hashctx); + memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ + free(ctx); + return 0; +} + |