The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#ifndef SvPVbyte
#define SvPVbyte SvPV
#endif

#include "src/sha.c"
#include "src/hmac.c"

static int ix2alg[] =
	{1,1,1,224,224,224,256,256,256,384,384,384,512,512,512,
	512224,512224,512224,512256,512256,512256};

MODULE = Digest::SHA		PACKAGE = Digest::SHA

PROTOTYPES: ENABLE

#include "src/sha.h"
#include "src/hmac.h"

#ifndef INT2PTR
#define INT2PTR(p, i) (p) (i)
#endif

#define MAX_WRITE_SIZE 16384

int
shaclose(s)
	SHA *	s

int
shadump(file, s)
	char *	file
	SHA *	s

SHA *
shadup(s)
	SHA *	s

SHA *
shaload(file)
	char *	file

SHA *
shaopen(alg)
	int	alg

void
sharewind(s)
	SHA *	s

unsigned long
shawrite(bitstr, bitcnt, s)
	unsigned char *	bitstr
	unsigned long	bitcnt
	SHA *	s

void
sha1(...)
ALIAS:
	Digest::SHA::sha1 = 0
	Digest::SHA::sha1_hex = 1
	Digest::SHA::sha1_base64 = 2
	Digest::SHA::sha224 = 3
	Digest::SHA::sha224_hex = 4
	Digest::SHA::sha224_base64 = 5
	Digest::SHA::sha256 = 6
	Digest::SHA::sha256_hex = 7
	Digest::SHA::sha256_base64 = 8
	Digest::SHA::sha384 = 9
	Digest::SHA::sha384_hex = 10
	Digest::SHA::sha384_base64 = 11
	Digest::SHA::sha512 = 12
	Digest::SHA::sha512_hex = 13
	Digest::SHA::sha512_base64 = 14
	Digest::SHA::sha512224 = 15
	Digest::SHA::sha512224_hex = 16
	Digest::SHA::sha512224_base64 = 17
	Digest::SHA::sha512256 = 18
	Digest::SHA::sha512256_hex = 19
	Digest::SHA::sha512256_base64 = 20
PREINIT:
	int i;
	unsigned char *data;
	STRLEN len;
	SHA *state;
	char *result;
PPCODE:
	if ((state = shaopen(ix2alg[ix])) == NULL)
		XSRETURN_UNDEF;
	for (i = 0; i < items; i++) {
		data = (unsigned char *) (SvPVbyte(ST(i), len));
		while (len > MAX_WRITE_SIZE) {
			shawrite(data, MAX_WRITE_SIZE << 3, state);
			data += MAX_WRITE_SIZE;
			len  -= MAX_WRITE_SIZE;
		}
		shawrite(data, len << 3, state);
	}
	shafinish(state);
	len = 0;
	if (ix % 3 == 0) {
		result = (char *) shadigest(state);
		len = shadsize(state);
	}
	else if (ix % 3 == 1)
		result = shahex(state);
	else
		result = shabase64(state);
	ST(0) = sv_2mortal(newSVpv(result, len));
	shaclose(state);
	XSRETURN(1);

void
hmac_sha1(...)
ALIAS:
	Digest::SHA::hmac_sha1 = 0
	Digest::SHA::hmac_sha1_hex = 1
	Digest::SHA::hmac_sha1_base64 = 2
	Digest::SHA::hmac_sha224 = 3
	Digest::SHA::hmac_sha224_hex = 4
	Digest::SHA::hmac_sha224_base64 = 5
	Digest::SHA::hmac_sha256 = 6
	Digest::SHA::hmac_sha256_hex = 7
	Digest::SHA::hmac_sha256_base64 = 8
	Digest::SHA::hmac_sha384 = 9
	Digest::SHA::hmac_sha384_hex = 10
	Digest::SHA::hmac_sha384_base64 = 11
	Digest::SHA::hmac_sha512 = 12
	Digest::SHA::hmac_sha512_hex = 13
	Digest::SHA::hmac_sha512_base64 = 14
	Digest::SHA::hmac_sha512224 = 15
	Digest::SHA::hmac_sha512224_hex = 16
	Digest::SHA::hmac_sha512224_base64 = 17
	Digest::SHA::hmac_sha512256 = 18
	Digest::SHA::hmac_sha512256_hex = 19
	Digest::SHA::hmac_sha512256_base64 = 20
PREINIT:
	int i;
	unsigned char *key;
	unsigned char *data;
	STRLEN len;
	HMAC *state;
	char *result;
PPCODE:
	key = (unsigned char *) (SvPVbyte(ST(items-1), len));
	if ((state = hmacopen(ix2alg[ix], key, len)) == NULL)
		XSRETURN_UNDEF;
	for (i = 0; i < items - 1; i++) {
		data = (unsigned char *) (SvPVbyte(ST(i), len));
		while (len > MAX_WRITE_SIZE) {
			hmacwrite(data, MAX_WRITE_SIZE << 3, state);
			data += MAX_WRITE_SIZE;
			len  -= MAX_WRITE_SIZE;
		}
		hmacwrite(data, len << 3, state);
	}
	hmacfinish(state);
	len = 0;
	if (ix % 3 == 0) {
		result = (char *) hmacdigest(state);
		len = shadsize(state->osha);
	}
	else if (ix % 3 == 1)
		result = hmachex(state);
	else
		result = hmacbase64(state);
	ST(0) = sv_2mortal(newSVpv(result, len));
	hmacclose(state);
	XSRETURN(1);

void
hashsize(self)
	SV *	self
ALIAS:
	Digest::SHA::hashsize = 0
	Digest::SHA::algorithm = 1
PREINIT:
	SHA *state;
	int result;
PPCODE:
	state = INT2PTR(SHA *, SvIV(SvRV(SvRV(self))));
	result = ix ? shaalg(state) : shadsize(state) << 3;
	ST(0) = sv_2mortal(newSViv(result));
	XSRETURN(1);

void
add(self, ...)
	SV *	self
PREINIT:
	int i;
	unsigned char *data;
	STRLEN len;
	SHA *state;
PPCODE:
	state = INT2PTR(SHA *, SvIV(SvRV(SvRV(self))));
	for (i = 1; i < items; i++) {
		data = (unsigned char *) (SvPVbyte(ST(i), len));
		while (len > MAX_WRITE_SIZE) {
			shawrite(data, MAX_WRITE_SIZE << 3, state);
			data += MAX_WRITE_SIZE;
			len  -= MAX_WRITE_SIZE;
		}
		shawrite(data, len << 3, state);
	}
	XSRETURN(1);

void
digest(self)
	SV *	self
ALIAS:
	Digest::SHA::digest = 0
	Digest::SHA::Hexdigest = 1
	Digest::SHA::B64digest = 2
PREINIT:
	STRLEN len;
	SHA *state;
	char *result;
PPCODE:
	state = INT2PTR(SHA *, SvIV(SvRV(SvRV(self))));
	shafinish(state);
	len = 0;
	if (ix == 0) {
		result = (char *) shadigest(state);
		len = shadsize(state);
	}
	else if (ix == 1)
		result = shahex(state);
	else
		result = shabase64(state);
	ST(0) = sv_2mortal(newSVpv(result, len));
	sharewind(state);
	XSRETURN(1);