The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include "src/chacha/chacha.c"
#include "src/chacha/poly1305.c"

MODULE = Crypt::OpenSSH::ChachaPoly		PACKAGE = Crypt::OpenSSH::ChachaPoly		

PROTOTYPES: ENABLE

Crypt::OpenSSH::ChachaPoly
new(class,key)
	SV *class
	SV *key
CODE:
	{
		STRLEN keysize;
		keysize = SvCUR(key);

		if (keysize != 16 && keysize != 32)
			croak ("The key must be 128 or 256 bits long");

		Newxz(RETVAL, 1, struct chacha_ctx);
		chacha_keysetup(RETVAL, (unsigned char *) SvPV_nolen(key), keysize*8);
	}
OUTPUT:
	RETVAL

SV *
encrypt(self,data)
	Crypt::OpenSSH::ChachaPoly self
	SV *data
ALIAS:
	decrypt = 1
CODE:
	{
		STRLEN size;
		void *bytes = SvPV(data,size);

		if (size) {
			RETVAL = NEWSV (0, size);
			SvPOK_only (RETVAL);
			SvCUR_set (RETVAL, size);
			chacha_encrypt_bytes(self, bytes, (unsigned char *) SvPV_nolen(RETVAL), (int) size);
		} else {
			RETVAL = newSVpv ("", 0);
		}

	}
OUTPUT:
	RETVAL

void
ivsetup(self,iv,counter)
	Crypt::OpenSSH::ChachaPoly self
	SV *iv
	SV *counter
CODE:
	{
		STRLEN iv_l ; unsigned char *iv_p = (unsigned char *) SvPVbyte (iv, iv_l);
		/* anything beyond 64 bits is ignored */
		if (iv_l < 8) {
			croak("ivsetup: iv must be 64 bits long!");
		}
		STRLEN counter_l ; unsigned char *counter_p = (unsigned char *) SvPVbyte (counter, counter_l);
		if (counter_l == 0)
			counter_p = NULL;
		/* anything beyond 8 chars is ignored */
		else if (counter_l < 8)
			croak ("ivsetup: counter must be 64 bits long!");
		chacha_ivsetup(self, iv_p, counter_p);
	}

void
DESTROY(self)
        Crypt::OpenSSH::ChachaPoly self
CODE:
        Safefree(self);

SV *
poly1305(self,data,key)
	Crypt::OpenSSH::ChachaPoly self
	SV *data
	SV *key
CODE:
	{
		STRLEN size;
		void *databytes = SvPV(data,size);

		STRLEN keysize;
		keysize = SvCUR(key);
		if (keysize != POLY1305_KEYLEN)
			croak("Key is incorrect size");
		void *keybytes = SvPV_nolen(key);

		RETVAL = NEWSV(0, POLY1305_TAGLEN);
		SvPOK_only (RETVAL);
		SvCUR_set (RETVAL, POLY1305_TAGLEN);
		poly1305_auth((unsigned char *) SvPV_nolen(RETVAL),databytes,(int) size,keybytes);
	}
OUTPUT:
	RETVAL