/** \ingroup signature
 * \file rpmio/digest.c
 */

#include "system.h"

#include "rpmio_internal.h"

#include <rpmbc.h>

#include "crc.h"
#include "md2.h"
#include "md4.h"
#include "sha224.h"
#include "rmd128.h"
#include "rmd160.h"
#include "rmd256.h"
#include "rmd320.h"
#include "salsa10.h"
#include "salsa20.h"
#include "tiger.h"

#include "debug.h"

#ifdef	SHA_DEBUG
#define	DPRINTF(_a)	fprintf _a
#else
#define	DPRINTF(_a)
#endif

/* Include Bob Jenkins lookup3 hash */
#define	_JLU3_jlu32l
#include "lookup3.c"

/*@access DIGEST_CTX@*/

/**
 * MD5/SHA1 digest private data.
 */
struct DIGEST_CTX_s {
/*@observer@*/
    const char * name;		/*!< Digest name. */
    size_t paramsize;		/*!< No. bytes of digest parameters. */
    size_t datasize;		/*!< No. bytes in block of plaintext data. */
    size_t digestsize;		/*!< No. bytes of digest. */
    int (*Reset) (void * param)
	/*@modifies param @*/;	/*!< Digest initialize. */
    int (*Update) (void * param, const byte * data, size_t size)
	/*@modifies param @*/;	/*!< Digest transform. */
    int (*Digest) (void * param, /*@out@*/ byte * digest)
	/*@modifies param, digest @*/;	/*!< Digest finish. */
    rpmDigestFlags flags;	/*!< Bit(s) to control digest operation. */
    void * param;		/*!< Digest parameters. */
};

DIGEST_CTX
rpmDigestDup(DIGEST_CTX octx)
{
    DIGEST_CTX nctx;
    nctx = memcpy(xcalloc(1, sizeof(*nctx)), octx, sizeof(*nctx));
    nctx->param = memcpy(xcalloc(1, nctx->paramsize), octx->param, nctx->paramsize);
    return nctx;
}

DIGEST_CTX
rpmDigestInit(pgpHashAlgo hashalgo, rpmDigestFlags flags)
{
    DIGEST_CTX ctx = xcalloc(1, sizeof(*ctx));
    int xx;

    ctx->flags = flags;

    switch (hashalgo) {
    case PGPHASHALGO_MD5:
	ctx->name = "MD5";
	ctx->digestsize = 128/8;
	ctx->datasize = 64;
/*@-sizeoftype@*/ /* FIX: union, not void pointer */
	ctx->paramsize = sizeof(md5Param);
/*@=sizeoftype@*/
	ctx->param = xcalloc(1, ctx->paramsize);
/*@-type@*/
	ctx->Reset = (int (*)(void *)) md5Reset;
	ctx->Update = (int (*)(void *, const byte *, size_t)) md5Update;
	ctx->Digest = (int (*)(void *, byte *)) md5Digest;
/*@=type@*/
	break;
    case PGPHASHALGO_SHA1:
	ctx->name = "SHA-1";
	ctx->digestsize = 160/8;
	ctx->datasize = 64;
/*@-sizeoftype@*/ /* FIX: union, not void pointer */
	ctx->paramsize = sizeof(sha1Param);
/*@=sizeoftype@*/
	ctx->param = xcalloc(1, ctx->paramsize);
/*@-type@*/
	ctx->Reset = (int (*)(void *)) sha1Reset;
	ctx->Update = (int (*)(void *, const byte *, size_t)) sha1Update;
	ctx->Digest = (int (*)(void *, byte *)) sha1Digest;
/*@=type@*/
	break;
    case PGPHASHALGO_RIPEMD128:
	ctx->name = "RIPEMD-128";
	ctx->digestsize = 128/8;
	ctx->datasize = 64;
/*@-sizeoftype@*/ /* FIX: union, not void pointer */
	ctx->paramsize = sizeof(rmd128Param);
/*@=sizeoftype@*/
	ctx->param = xcalloc(1, ctx->paramsize);
/*@-type@*/
	ctx->Reset = (int (*)(void *)) rmd128Reset;
	ctx->Update = (int (*)(void *, const byte *, size_t)) rmd128Update;
	ctx->Digest = (int (*)(void *, byte *)) rmd128Digest;
/*@=type@*/
	break;
    case PGPHASHALGO_RIPEMD160:
	ctx->name = "RIPEMD-160";
	ctx->digestsize = 160/8;
	ctx->datasize = 64;
/*@-sizeoftype@*/ /* FIX: union, not void pointer */
	ctx->paramsize = sizeof(rmd160Param);
/*@=sizeoftype@*/
	ctx->param = xcalloc(1, ctx->paramsize);
/*@-type@*/
	ctx->Reset = (int (*)(void *)) rmd160Reset;
	ctx->Update = (int (*)(void *, const byte *, size_t)) rmd160Update;
	ctx->Digest = (int (*)(void *, byte *)) rmd160Digest;
/*@=type@*/
	break;
    case PGPHASHALGO_RIPEMD256:
	ctx->name = "RIPEMD-256";
	ctx->digestsize = 256/8;
	ctx->datasize = 64;
/*@-sizeoftype@*/ /* FIX: union, not void pointer */
	ctx->paramsize = sizeof(rmd256Param);
/*@=sizeoftype@*/
	ctx->param = xcalloc(1, ctx->paramsize);
/*@-type@*/
	ctx->Reset = (int (*)(void *)) rmd256Reset;
	ctx->Update = (int (*)(void *, const byte *, size_t)) rmd256Update;
	ctx->Digest = (int (*)(void *, byte *)) rmd256Digest;
/*@=type@*/
	break;
    case PGPHASHALGO_RIPEMD320:
	ctx->name = "RIPEMD-320";
	ctx->digestsize = 320/8;
	ctx->datasize = 64;
/*@-sizeoftype@*/ /* FIX: union, not void pointer */
	ctx->paramsize = sizeof(rmd320Param);
/*@=sizeoftype@*/
	ctx->param = xcalloc(1, ctx->paramsize);
/*@-type@*/
	ctx->Reset = (int (*)(void *)) rmd320Reset;
	ctx->Update = (int (*)(void *, const byte *, size_t)) rmd320Update;
	ctx->Digest = (int (*)(void *, byte *)) rmd320Digest;
/*@=type@*/
	break;
    case PGPHASHALGO_SALSA10:
	ctx->name = "SALSA-10";
	ctx->digestsize = 512/8;
	ctx->datasize = 64;
/*@-sizeoftype@*/ /* FIX: union, not void pointer */
	ctx->paramsize = sizeof(salsa10Param);
/*@=sizeoftype@*/
	ctx->param = xcalloc(1, ctx->paramsize);
/*@-type@*/
	ctx->Reset = (int (*)(void *)) salsa10Reset;
	ctx->Update = (int (*)(void *, const byte *, size_t)) salsa10Update;
	ctx->Digest = (int (*)(void *, byte *)) salsa10Digest;
/*@=type@*/
	break;
    case PGPHASHALGO_SALSA20:
	ctx->name = "SALSA-20";
	ctx->digestsize = 512/8;
	ctx->datasize = 64;
/*@-sizeoftype@*/ /* FIX: union, not void pointer */
	ctx->paramsize = sizeof(salsa20Param);
/*@=sizeoftype@*/
	ctx->param = xcalloc(1, ctx->paramsize);
/*@-type@*/
	ctx->Reset = (int (*)(void *)) salsa20Reset;
	ctx->Update = (int (*)(void *, const byte *, size_t)) salsa20Update;
	ctx->Digest = (int (*)(void *, byte *)) salsa20Digest;
/*@=type@*/
	break;
    case PGPHASHALGO_TIGER192:
	ctx->name = "TIGER-192";
	ctx->digestsize = 192/8;
	ctx->datasize = 64;
/*@-sizeoftype@*/ /* FIX: union, not void pointer */
	ctx->paramsize = sizeof(tigerParam);
/*@=sizeoftype@*/
	ctx->param = xcalloc(1, ctx->paramsize);
/*@-type@*/
	ctx->Reset = (int (*)(void *)) tigerReset;
	ctx->Update = (int (*)(void *, const byte *, size_t)) tigerUpdate;
	ctx->Digest = (int (*)(void *, byte *)) tigerDigest;
/*@=type@*/
	break;
    case PGPHASHALGO_MD2:
	ctx->name = "MD2";
	ctx->digestsize = 128/8;
	ctx->datasize = 16;
/*@-sizeoftype@*/ /* FIX: union, not void pointer */
	ctx->paramsize = sizeof(md2Param);
/*@=sizeoftype@*/
	ctx->param = xcalloc(1, ctx->paramsize);
/*@-type@*/
	ctx->Reset = (int (*)(void *)) md2Reset;
	ctx->Update = (int (*)(void *, const byte *, size_t)) md2Update;
	ctx->Digest = (int (*)(void *, byte *)) md2Digest;
/*@=type@*/
	break;
    case PGPHASHALGO_MD4:
	ctx->name = "MD4";
	ctx->digestsize = 128/8;
	ctx->datasize = 64;
/*@-sizeoftype@*/ /* FIX: union, not void pointer */
	ctx->paramsize = sizeof(md4Param);
/*@=sizeoftype@*/
	ctx->param = xcalloc(1, ctx->paramsize);
/*@-type@*/
	ctx->Reset = (int (*)(void *)) md4Reset;
	ctx->Update = (int (*)(void *, const byte *, size_t)) md4Update;
	ctx->Digest = (int (*)(void *, byte *)) md4Digest;
/*@=type@*/
	break;
    case PGPHASHALGO_CRC32:
	ctx->name = "CRC-32";
	ctx->digestsize = 32/8;
	ctx->datasize = 8;
	{   sum32Param * mp = xcalloc(1, sizeof(*mp));
/*@-type @*/
	    mp->update = (uint32_t (*)(uint32_t, const uint8_t *, size_t)) __crc32;
#if defined(ZLIB_H)
#if defined(HAVE_ZLIB_CRC32_COMBINE)
	    mp->combine = (uint32_t (*)(uint32_t, uint32_t, size_t)) __crc32_combine;
#endif
#endif
/*@=type @*/
	    ctx->paramsize = sizeof(*mp);
	    ctx->param = mp;
	}
/*@-type@*/
	ctx->Reset = (int (*)(void *)) sum32Reset;
	ctx->Update = (int (*)(void *, const byte *, size_t)) sum32Update;
	ctx->Digest = (int (*)(void *, byte *)) sum32Digest;
/*@=type@*/
	break;
    case PGPHASHALGO_ADLER32:
	ctx->name = "ADLER-32";
	ctx->digestsize = 32/8;
	ctx->datasize = 8;
	{   sum32Param * mp = xcalloc(1, sizeof(*mp));
/*@-type @*/
#if defined(ZLIB_H)
	    mp->update = (uint32_t (*)(uint32_t, const uint8_t *, size_t)) __adler32;
#if defined(HAVE_ZLIB_ADLER32_COMBINE)
	    mp->combine = (uint32_t (*)(uint32_t, uint32_t, size_t)) __adler32_combine;
#endif
#endif
/*@=type @*/
	    ctx->paramsize = sizeof(*mp);
	    ctx->param = mp;
	}
/*@-type@*/
	ctx->Reset = (int (*)(void *)) sum32Reset;
	ctx->Update = (int (*)(void *, const byte *, size_t)) sum32Update;
	ctx->Digest = (int (*)(void *, byte *)) sum32Digest;
/*@=type@*/
	break;
    case PGPHASHALGO_JLU32:
	ctx->name = "JLU-32";
	ctx->digestsize = 32/8;
	ctx->datasize = 8;
	{   sum32Param * mp = xcalloc(1, sizeof(*mp));
/*@-type @*/
	    mp->update = (uint32_t (*)(uint32_t, const byte *, size_t)) jlu32l;
/*@=type @*/
	    ctx->paramsize = sizeof(*mp);
	    ctx->param = mp;
	}
/*@-type@*/
	ctx->Reset = (int (*)(void *)) sum32Reset;
	ctx->Update = (int (*)(void *, const byte *, size_t)) sum32Update;
	ctx->Digest = (int (*)(void *, byte *)) sum32Digest;
/*@=type@*/
	break;
    case PGPHASHALGO_CRC64:
	ctx->name = "CRC-64";
	ctx->digestsize = 64/8;
	ctx->datasize = 8;
	{   sum64Param * mp = xcalloc(1, sizeof(*mp));
/*@-type@*/
	    mp->update = (uint64_t (*)(uint64_t, const uint8_t *, size_t)) __crc64;
	    mp->combine = (uint64_t (*)(uint64_t, uint64_t, size_t)) __crc64_combine;
/*@=type@*/
	    ctx->paramsize = sizeof(*mp);
	    ctx->param = mp;
	}
/*@-type@*/
	ctx->Reset = (int (*)(void *)) sum64Reset;
	ctx->Update = (int (*)(void *, const byte *, size_t)) sum64Update;
	ctx->Digest = (int (*)(void *, byte *)) sum64Digest;
/*@=type@*/
	break;
#if defined(HAVE_BEECRYPT_API_H)
    case PGPHASHALGO_SHA224:
	ctx->name = "SHA-224";
	ctx->digestsize = 224/8;
	ctx->datasize = 64;
/*@-sizeoftype@*/ /* FIX: union, not void pointer */
	ctx->paramsize = sizeof(sha224Param);
/*@=sizeoftype@*/
	ctx->param = xcalloc(1, ctx->paramsize);
/*@-type@*/
	ctx->Reset = (int (*)(void *)) sha224Reset;
	ctx->Update = (int (*)(void *, const byte *, size_t)) sha224Update;
	ctx->Digest = (int (*)(void *, byte *)) sha224Digest;
/*@=type@*/
	break;
    case PGPHASHALGO_SHA256:
	ctx->name = "SHA-256";
	ctx->digestsize = 256/8;
	ctx->datasize = 64;
/*@-sizeoftype@*/ /* FIX: union, not void pointer */
	ctx->paramsize = sizeof(sha256Param);
/*@=sizeoftype@*/
	ctx->param = xcalloc(1, ctx->paramsize);
/*@-type@*/
	ctx->Reset = (int (*)(void *)) sha256Reset;
	ctx->Update = (int (*)(void *, const byte *, size_t)) sha256Update;
	ctx->Digest = (int (*)(void *, byte *)) sha256Digest;
/*@=type@*/
	break;
    case PGPHASHALGO_SHA384:
	ctx->name = "SHA-384";
	ctx->digestsize = 384/8;
	ctx->datasize = 128;
/*@-sizeoftype@*/ /* FIX: union, not void pointer */
	ctx->paramsize = sizeof(sha384Param);
/*@=sizeoftype@*/
	ctx->param = xcalloc(1, ctx->paramsize);
/*@-type@*/
	ctx->Reset = (int (*)(void *)) sha384Reset;
	ctx->Update = (int (*)(void *, const byte *, size_t)) sha384Update;
	ctx->Digest = (int (*)(void *, byte *)) sha384Digest;
/*@=type@*/
	break;
    case PGPHASHALGO_SHA512:
	ctx->name = "SHA-512";
	ctx->digestsize = 512/8;
	ctx->datasize = 128;
/*@-sizeoftype@*/ /* FIX: union, not void pointer */
	ctx->paramsize = sizeof(sha512Param);
/*@=sizeoftype@*/
	ctx->param = xcalloc(1, ctx->paramsize);
/*@-type@*/
	ctx->Reset = (int (*)(void *)) sha512Reset;
	ctx->Update = (int (*)(void *, const byte *, size_t)) sha512Update;
	ctx->Digest = (int (*)(void *, byte *)) sha512Digest;
/*@=type@*/
	break;
#endif
    case PGPHASHALGO_HAVAL_5_160:
    default:
	free(ctx);
	return NULL;
	/*@notreached@*/ break;
    }

    xx = (*ctx->Reset) (ctx->param);

DPRINTF((stderr, "*** Init(%x) ctx %p param %p\n", flags, ctx, ctx->param));
    return ctx;
}

/*@-mustmod@*/ /* LCL: ctx->param may be modified, but ctx is abstract @*/
int
rpmDigestUpdate(DIGEST_CTX ctx, const void * data, size_t len)
{
    if (ctx == NULL)
	return -1;

DPRINTF((stderr, "*** Update(%p,%p,%d) param %p \"%s\"\n", ctx, data, len, ctx->param, ((char *)data)));
    return (*ctx->Update) (ctx->param, data, len);
}
/*@=mustmod@*/

int
rpmDigestFinal(DIGEST_CTX ctx, void * datap, size_t *lenp, int asAscii)
{
    byte * digest;
    char * t;

    if (ctx == NULL)
	return -1;
    digest = xmalloc(ctx->digestsize);

DPRINTF((stderr, "*** Final(%p,%p,%p,%d) param %p digest %p\n", ctx, datap, lenp, asAscii, ctx->param, digest));
/*@-noeffectuncon@*/ /* FIX: check rc */
    (void) (*ctx->Digest) (ctx->param, digest);
/*@=noeffectuncon@*/

    /* Return final digest. */
    if (!asAscii) {
	if (lenp) *lenp = ctx->digestsize;
	if (datap) {
	    *(byte **)datap = digest;
	    digest = NULL;
	}
    } else {
	if (lenp) *lenp = (2*ctx->digestsize) + 1;
	if (datap) {
	    const byte * s = (const byte *) digest;
	    static const char hex[] = "0123456789abcdef";
	    size_t i;

	    *(char **)datap = t = xmalloc((2*ctx->digestsize) + 1);
	    for (i = 0 ; i < ctx->digestsize; i++) {
		*t++ = hex[ (unsigned)((*s >> 4) & 0x0f) ];
		*t++ = hex[ (unsigned)((*s++   ) & 0x0f) ];
	    }
	    *t = '\0';
	}
    }
    if (digest) {
	memset(digest, 0, ctx->digestsize);	/* In case it's sensitive */
	free(digest);
    }
    memset(ctx->param, 0, ctx->paramsize);	/* In case it's sensitive */
    free(ctx->param);
    memset(ctx, 0, sizeof(*ctx));	/* In case it's sensitive */
    free(ctx);
    return 0;
}
