The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

/* try to be compatible with older perls */
/* SvPV_nolen() macro first defined in 5.005_55 */
/* this is slow, not threadsafe, but works */
#include "patchlevel.h"
#if (PATCHLEVEL == 4) || ((PATCHLEVEL == 5) && (SUBVERSION < 55))
static STRLEN nolen_na;
# define SvPV_nolen(sv) SvPV ((sv), nolen_na)
#endif

#include "aes.h"
#include "twofish.c"

typedef struct cryptstate {
  keyInstance ki;
  cipherInstance ci;
} *Crypt__Twofish2;

MODULE = Crypt::Twofish2		PACKAGE = Crypt::Twofish2

PROTOTYPES: ENABLE

BOOT:
{
	HV *stash = gv_stashpv ("Crypt::Twofish2", 0);

	newCONSTSUB (stash, "keysize",   newSViv (32));
	newCONSTSUB (stash, "blocksize", newSViv (16));
	newCONSTSUB (stash, "MODE_ECB",  newSViv (MODE_ECB));
	newCONSTSUB (stash, "MODE_CBC",  newSViv (MODE_CBC));
	newCONSTSUB (stash, "MODE_CFB1", newSViv (MODE_CFB1));
}

Crypt::Twofish2
new(class, key, mode=MODE_ECB)
	SV *	class
	SV *	key
        int	mode
        CODE:
        {
          STRLEN keysize;
          
          if (!SvPOK (key))
            croak ("key must be a string scalar");

          keysize = SvCUR(key);

          if (keysize != 16 && keysize != 24 && keysize != 32)
            croak ("wrong key length: key must be 128, 192 or 256 bits long");
          if (mode != MODE_ECB && mode != MODE_CBC && mode != MODE_CFB1)
            croak ("illegal mode: mode must be MODE_ECB, MODE_2 or MODE_CFB1");

          Newz (0, RETVAL, 1, struct cryptstate); /* Newz required for defined IV */
          
          if (makeKey (&RETVAL->ki, DIR_ENCRYPT, keysize*8, SvPV_nolen(key)) != TRUE)
            croak ("Crypt::Twofish2: makeKey failed, please report!");
          if (cipherInit (&RETVAL->ci, mode, 0) != TRUE) /* no IV supported (yet) */
            croak ("Crypt::Twofish2: makeKey failed, please report!");
        }         
	OUTPUT:
        RETVAL

SV *
encrypt(self, data)
 	Crypt::Twofish2 self
        SV *	data
        ALIAS:
        	decrypt = 1
        CODE:
        {
          SV *res;
          STRLEN size;
          void *rawbytes = SvPV(data,size);

          if (size)
            {
              if (size % (BLOCK_SIZE >> 3))
                croak ("encrypt: datasize not multiple of blocksize (%d bits)", BLOCK_SIZE);

              RETVAL = NEWSV (0, size);
              SvPOK_only (RETVAL);
              (SvPVX (RETVAL))[size] = 0;
              SvCUR_set (RETVAL, size);

              if ((ix ? blockDecrypt : blockEncrypt)
                    (&self->ci, &self->ki, rawbytes, size << 3, (void *)SvPV_nolen(RETVAL)) < 0)
                croak ("block(De|En)crypt: unknown error, please report");
            }
          else
            RETVAL = newSVpv ("", 0);
        }
	OUTPUT:
        RETVAL

void
DESTROY(self)
        Crypt::Twofish2 self
        CODE:
        Safefree(self);