#include "xs/openssl.h"

MODULE = Crypt::Keyczar	PACKAGE = Crypt::Keyczar::HmacEngine


Crypt::Keyczar::HmacEngine
new(class, type, key)
	SV *class
	const char *type
	SV *key
	CODE:
	{
	    unsigned char *k;
	    STRLEN l;
	    const EVP_MD *md;
	    PERL_UNUSED_VAR(class);

	    ENGINE_load_builtin_engines();
	    ENGINE_register_all_complete();

	    if (strcasecmp(type, "SHA1") == 0) {
                md = EVP_sha1();
            }
            else if (strcasecmp(type, "SHA224") == 0) {
                 md = EVP_sha224();
            }
            else if (strcasecmp(type, "SHA256") == 0) {
                 md = EVP_sha256();
            }
            else if (strcasecmp(type, "SHA384") == 0) {
                 md = EVP_sha384();
            }
            else if (strcasecmp(type, "SHA512") == 0) {
                 md = EVP_sha512();
            }
            else {
	        croak("unsupported digest name: %s", type);
	    }

	    Newz(0, RETVAL, 1, struct Crypt__Keyczar__HmacEngine_class);
	    RETVAL->context = HMAC_CTX_new();

	    k = (unsigned char *)SvPV(key, l);
            HMAC_Init_ex(RETVAL->context, k, l, md, NULL);
	}
	OUTPUT:
	    RETVAL


void
DESTROY(self)
	Crypt::Keyczar::HmacEngine self
	CODE:
	{
	    if (self->context != NULL) {
	        HMAC_CTX_free(self->context);
                self->context = NULL;
	    }
	    Safefree(self);
            self = NULL;
	}


int
digest_size(self)
	Crypt::Keyczar::HmacEngine self
	CODE:
	{
	    RETVAL = EVP_MD_size(EVP_MD_CTX_md((const EVP_MD_CTX *)self->context));
	}
	OUTPUT:
	    RETVAL


void
update(self, ...)
	Crypt::Keyczar::HmacEngine self
	CODE:
	{
	    int i;
	    SV *data;
	    STRLEN l;
	    unsigned char *in;

	    for (i = 1; i < items; i++) {
	        data = ST(i);
	        in = (unsigned char *)SvPV(data, l);
	        HMAC_Update(self->context, (const unsigned char *)in, l);
            }
	}


SV *
sign(self)
	Crypt::Keyczar::HmacEngine self
	CODE:
	{
	    unsigned char md[EVP_MAX_MD_SIZE];
	    unsigned int l;

	    HMAC_Final(self->context, md, &l);
            RETVAL = newSVpv((const char *)md, l);
	}
	OUTPUT:
	    RETVAL


int
verify(self, mac)
	Crypt::Keyczar::HmacEngine self
	SV *mac
	CODE:
	{
	    unsigned char md[EVP_MAX_MD_SIZE];
	    unsigned int l;
	    STRLEN in_l;
	    unsigned char *in;

	    in = (unsigned char *)SvPV(mac, in_l);
	    if (in_l != EVP_MD_size(EVP_MD_CTX_md((const EVP_MD_CTX *)self->context))) {
	        RETVAL = 0;
	    }
	    else {
	        HMAC_Final(self->context, md, &l);
	        RETVAL = (memcmp(md, in, in_l) == 0);
	    }
	}
	OUTPUT:
	    RETVAL


int
is_supported(class, type)
	SV *class
	const char *type
	CODE:
	{
            PERL_UNUSED_VAR(class);

	    if (strcasecmp(type, "SHA1") == 0) {
                RETVAL = 1;
            }
#if defined(SHA224_DIGEST_LENGTH)
            else if (strcasecmp(type, "SHA224") == 0) {
                RETVAL = 1;
            }
#endif
#if defined(SHA256_DIGEST_LENGTH)
            else if (strcasecmp(type, "SHA256") == 0) {
                RETVAL = 1;
            }
#endif
#if defined(SHA384_DIGEST_LENGTH)
            else if (strcasecmp(type, "SHA384") == 0) {
                RETVAL = 1;
            }
#endif
#if defined(SHA512_DIGEST_LENGTH)
            else if (strcasecmp(type, "SHA512") == 0) {
                RETVAL = 1;
            }
#endif
            else {
	        RETVAL = 0;
            } 
	}
	OUTPUT:
	    RETVAL