MODULE = CryptX         PACKAGE = Crypt::Cipher

Crypt::Cipher
_new(cipher_name, key, rounds=0)
        char * cipher_name
        SV * key
        int rounds
    CODE:
    {
        STRLEN key_len;
        unsigned char *key_data=NULL;
        int rv;
        int id;

        if (!SvPOK (key)) croak("FATAL: key must be string scalar");
        key_data = (unsigned char *)SvPVbyte(key, key_len);

        id = find_cipher(cipher_name);
        if (id == -1) croak("FATAL: find_cipfer failed for '%s'", cipher_name);

        Newz(0, RETVAL, 1, struct cipher_struct);
        if (!RETVAL) croak("FATAL: Newz failed");

        RETVAL->id = id;
        RETVAL->desc = &cipher_descriptor[id];
        rv = RETVAL->desc->setup(key_data, (int)key_len, rounds, &RETVAL->skey);
        if (rv != CRYPT_OK) {
          Safefree(RETVAL);
          croak("FATAL: cipher setup failed: %s", error_to_string(rv));
        }
    }
    OUTPUT:
        RETVAL

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

int
_max_keysize(self, ...)
        Crypt::Cipher self
    CODE:
        RETVAL = self->desc->max_key_length;
    OUTPUT:
        RETVAL

int
_min_keysize(self, ...)
        Crypt::Cipher self
    CODE:
        RETVAL = self->desc->min_key_length;
    OUTPUT:
        RETVAL

int
_blocksize(self, ...)
        Crypt::Cipher self
    CODE:
        RETVAL = self->desc->block_length;
    OUTPUT:
        RETVAL

int
_default_rounds(self, ...)
        Crypt::Cipher self
    CODE:
        RETVAL = self->desc->default_rounds;
    OUTPUT:
        RETVAL

SV *
encrypt(self, data)
        Crypt::Cipher self
        SV * data
    CODE:
    {
        int rv;
        STRLEN len;
        void *plaintext = SvPVbyte(data, len);

        if (len == 0) {
          RETVAL = newSVpvn("", 0);
        }
        else if (len == self->desc->block_length) {
          RETVAL = NEWSV(0, len);
          SvPOK_only(RETVAL);
          SvCUR_set(RETVAL, len);
          rv = self->desc->ecb_encrypt((unsigned char *)plaintext, (unsigned char *)SvPVX(RETVAL), &self->skey);
          if (rv!=CRYPT_OK) {
            SvREFCNT_dec(RETVAL);
            croak("FATAL: encrypt failed: %s", error_to_string(rv));
          }
        }
        else {
          croak ("FATAL: input size not equal to blocksize (%d)", self->desc->block_length);
        }
    }
    OUTPUT:
        RETVAL

SV *
decrypt(self, data)
        Crypt::Cipher self
        SV * data
    CODE:
    {
        int rv;
        STRLEN len;
        void *ciphertext = SvPVbyte(data, len);

        if (len == 0) {
          RETVAL = newSVpvn("", 0);
        }
        else if (len == self->desc->block_length) {
          RETVAL = NEWSV(0, len);
          SvPOK_only(RETVAL);
          SvCUR_set(RETVAL, len);
          rv = self->desc->ecb_decrypt((unsigned char *)ciphertext, (unsigned char *)SvPVX(RETVAL), &self->skey);
          if (rv!=CRYPT_OK) {
            SvREFCNT_dec(RETVAL);
            croak("FATAL: decrypt failed: %s", error_to_string(rv));
          }
        }
        else {
          croak ("FATAL: input size not equal to blocksize (%d)", self->desc->block_length);
        }
    }
    OUTPUT:
        RETVAL

int
_block_length_by_name(cipher_name)
        char * cipher_name
    CODE:
    {
        int rv, id;

        id = find_cipher(cipher_name);
        if (id == -1) croak("FATAL: find_cipfer failed for '%s'", cipher_name);

        rv = cipher_descriptor[id].block_length;
        if (!rv) XSRETURN_UNDEF;
        RETVAL = rv;
    }
    OUTPUT:
        RETVAL

int
_min_key_length_by_name(cipher_name)
        char * cipher_name
    CODE:
    {
        int rv, id;

        id = find_cipher(cipher_name);
        if (id == -1) croak("FATAL: find_cipfer failed for '%s'", cipher_name);

        rv = cipher_descriptor[id].min_key_length;
        if (!rv) XSRETURN_UNDEF;
        RETVAL = rv;
    }
    OUTPUT:
        RETVAL

int
_max_key_length_by_name(cipher_name)
        char * cipher_name
    CODE:
    {
        int rv, id;

        id = find_cipher(cipher_name);
        if (id == -1) croak("FATAL: find_cipfer failed for '%s'", cipher_name);

        rv = cipher_descriptor[id].max_key_length;
        if (!rv) XSRETURN_UNDEF;
        RETVAL = rv;
    }
    OUTPUT:
        RETVAL

int
_default_rounds_by_name(cipher_name)
        char * cipher_name
    CODE:
    {
        int rv, id;

        id = find_cipher(cipher_name);
        if (id == -1) croak("FATAL: find_cipfer failed for '%s'", cipher_name);

        rv = cipher_descriptor[id].default_rounds;
        if (!rv) XSRETURN_UNDEF;
        RETVAL = rv;
    }
    OUTPUT:
        RETVAL
