#include "xs/openssl.h"


MODULE = Crypt::Keyczar		PACKAGE = Crypt::Keyczar::RsaPublicKeyEngine

Crypt::Keyczar::RsaPublicKeyEngine
new(class, modulus, publicExponent)
	SV *class
	SV *modulus
	SV *publicExponent
	CODE:
	{
	    RSA *rsa;
	    STRLEN l;
	    unsigned char *in;
	    BIGNUM *e, *n;

	    PERL_UNUSED_VAR(class);
	    if ((rsa = RSA_new()) == NULL) {
	        crypt__keyczar__util__croak_openssl();
	        /* NOTREACHED */
	    }
	    in = (unsigned char *)SvPV(modulus, l);
	    n = BN_bin2bn((const unsigned char *)in, l, NULL);
            if (n == NULL) {
	        RSA_free(rsa);
	        crypt__keyczar__util__croak_openssl();
	        /* NOTREACHED */
	    }
	    in = (unsigned char *)SvPV(publicExponent, l);
	    e = BN_bin2bn((const unsigned char *)in, l, NULL);
            if (e == NULL) {
	        RSA_free(rsa);
	        crypt__keyczar__util__croak_openssl();
	        /* NOTREACHED */
	    }
	    if (RSA_set0_key(rsa, n, e, NULL) == 0) {
	        RSA_free(rsa);
	        crypt__keyczar__util__croak_openssl();
	        /* NOTREACHED */
	    }

	    Newz(0, RETVAL, 1, struct Crypt__Keyczar__RsaPublicKeyEngine_class);
	    RETVAL->rsa = rsa;
	}
	OUTPUT:
	    RETVAL


void
DESTROY(self)
	Crypt::Keyczar::RsaPublicKeyEngine self
	CODE:
	{
	    if (self->rsa != NULL) {
	        RSA_free(self->rsa);
	        self->rsa = NULL;
	    }
	    Safefree(self);
	}


SV *
init(self, ...)
	Crypt::Keyczar::RsaPublicKeyEngine self
	CODE:
	{
	    RETVAL = newSVpv("", 0);
	}
	OUTPUT:
	    RETVAL

SV *
encrypt(self, data)
	Crypt::Keyczar::RsaPublicKeyEngine self
	SV *data
	CODE:
	{
	    STRLEN in_l;
	    unsigned char *in, *out;
	    int size = RSA_size(self->rsa);
	    int out_l;

	    Newz(0, out, size, unsigned char);
	    in = (unsigned char *)SvPV(data, in_l);
	    out_l = RSA_public_encrypt(in_l, in, out, self->rsa, RSA_PKCS1_OAEP_PADDING);
	    if (out_l == -1) {
	        Safefree(out);
	        crypt__keyczar__util__croak_openssl();
	        /* NOTREACHED */
	    }
	    RETVAL = newSVpv((char *)out, out_l);
	    Safefree(out);
	}
	OUTPUT:
	    RETVAL


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

	    if (self->message == NULL) {
	        self->message = EVP_MD_CTX_create();
	        if (!EVP_DigestInit_ex(self->message, EVP_sha1(), NULL)) {
	            croak("cannot initialize SHA1 context");
	        }
	    }

	    for (i = 1; i < items; i++) {
	        data = ST(i);
	        in = (unsigned char *)SvPV(data, l);
	        if (!EVP_DigestUpdate(self->message, in, l)) {
	            croak("cannot update SHA1 context");
	        }
	    }
	}


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

	    if (self->message == NULL) {
	        RETVAL = 0;
	        return;
	    }
	    EVP_DigestFinal_ex(self->message, md, &l);
	    in = (unsigned char *)SvPV(mac, in_l);
	    rc = RSA_verify(NID_sha1, md, l, in, in_l, self->rsa);
	    if (rc == -1) {
	        crypt__keyczar__util__croak_openssl();
	        /* NOTREACHED */
	    }
	    RETVAL = rc == 1;
	}
	OUTPUT:
	    RETVAL


int
digest_size(self)
	Crypt::Keyczar::RsaPublicKeyEngine self
	CODE:
	{
	    RETVAL = RSA_size(self->rsa);
	}
	OUTPUT:
	    RETVAL