/* CRYPTSCM.C Provides the "glue" which allows cryptlib routines to be
 * called from SCM. */

#ifdef HAVE_CONFIG_H
#include "scmconfig.h"
#endif

#include "scm.h"
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif

#ifdef HAVE_SYS_FILIO_H
#include <sys/filio.h>
#endif

#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif

#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif

/* #include <capi.h> */ /* Commented out temporarily by Foner. */

#define MOD "cryptlib:"

/* SELECT() is BSD select() on the current operating system */
#ifdef hpux /* (fd_set *)s are replaced with (int *)s, for some reason */
#define SELECT(num, rd, wr, ex, to) \
  select(num, (int *)rd, (int *)wr, (int *)ex, to)
#else
#define SELECT(num, rd, wr, ex, to) select(num, rd, wr, ex, to)
#endif

/* in mak0strfrom.c */
char *mak0strfrom(SCM);

     /******************/
     /* cryptlib enums */
     /******************/

typedef struct {
  char *name;
  int  value;
} CryptEnum;

#define ENUM0(x) {"CRYPT_" #x, CRYPT_##x},
#define ENUM(x) ENUM0(x)

CryptEnum cryptEnums[] = {
  ENUM(ALGO_NONE)
  ENUM(ALGO_DES)
  ENUM(ALGO_3DES)
  ENUM(ALGO_IDEA)
  ENUM(ALGO_CAST)
  ENUM(ALGO_RC2)
  ENUM(ALGO_RC4)
  ENUM(ALGO_RC5)
  ENUM(ALGO_SAFER)
  ENUM(ALGO_BLOWFISH)
  ENUM(ALGO_GOST)
  ENUM(ALGO_SKIPJACK)
  ENUM(ALGO_MDCSHS)
  ENUM(ALGO_DH)
  ENUM(ALGO_RSA)
  ENUM(ALGO_DSA)
  ENUM(ALGO_ELGAMAL)
  ENUM(ALGO_MD2)
  ENUM(ALGO_MD4)
  ENUM(ALGO_MD5)
  ENUM(ALGO_SHA)
  ENUM(ALGO_RIPEMD160)
  ENUM(ALGO_HMAC_MD5)
  ENUM(ALGO_HMAC_SHA)
  ENUM(ALGO_HMAC_RIPEMD160)
  ENUM(ALGO_LAST)
  ENUM(ALGO_FIRST_CONVENTIONAL)
  ENUM(ALGO_LAST_CONVENTIONAL)
  ENUM(ALGO_FIRST_PKC)
  ENUM(ALGO_LAST_PKC)
  ENUM(ALGO_FIRST_HASH)
  ENUM(ALGO_LAST_HASH)
  ENUM(ALGO_FIRST_MAC)
  ENUM(ALGO_LAST_MAC)
  ENUM(MODE_NONE)
  ENUM(MODE_STREAM)
  ENUM(MODE_ECB)
  ENUM(MODE_CBC)
  ENUM(MODE_CFB)
  ENUM(MODE_OFB)
  ENUM(MODE_PCBC)
  ENUM(MODE_PKC)
  ENUM(MODE_LAST)
  ENUM(MODE_FIRST_CONVENTIONAL)
  ENUM(MODE_LAST_CONVENTIONAL)
  ENUM(OBJECT_NONE)
  ENUM(OBJECT_ENCRYPTED_KEY)
  ENUM(OBJECT_PKCENCRYPTED_KEY)
  ENUM(OBJECT_SIGNATURE)
  ENUM(OBJECT_LAST)
  ENUM(KEYSET_NONE)
  ENUM(KEYSET_FILE)
  ENUM(KEYSET_LDAP)
  ENUM(KEYSET_SMARTCARD)
  ENUM(KEYSET_ODBC)
  ENUM(KEYSET_ORACLE)
  ENUM(KEYSET_POSTGRES)
  ENUM(KEYSET_LAST)
  ENUM(KEYID_NONE)
  ENUM(KEYID_NAME)
  ENUM(KEYID_EMAIL)
  ENUM(KEYID_OBJECT)
  ENUM(KEYID_LAST)
  ENUM(ENVTYPE_NONE)
  ENUM(ENVTYPE_CRYPTLIB)
  ENUM(ENVTYPE_SMIME)
  ENUM(ENVTYPE_PGP)
  ENUM(ENVTYPE_LAST)
  ENUM(RESOURCE_NONE)
  ENUM(RESOURCE_SIGNATURE)
  ENUM(RESOURCE_PASSWORD)
  ENUM(RESOURCE_KEY)
  ENUM(RESOURCE_PKCKEY)
  ENUM(RESOURCE_SESSIONKEY)
  ENUM(RESOURCE_HASH)
  ENUM(RESOURCE_MAC)
  ENUM(RESOURCE_PADDING)
  ENUM(RESOURCE_COMPRESSION)
  ENUM(RESOURCE_LAST)
  ENUM(KEYFUNCTION_NONE)
  ENUM(KEYFUNCTION_ENCRYPT)
  ENUM(KEYFUNCTION_DECRYPT)
  ENUM(KEYFUNCTION_SIGCHECK)
  ENUM(KEYFUNCTION_SIGNATURE)
  ENUM(KEYFUNCTION_LAST)
  ENUM(CERTINFO_NONE)
  ENUM(CERTINFO_SERIALNUMBER)
  ENUM(CERTINFO_VALIDFROM)
  ENUM(CERTINFO_VALIDTO)
  ENUM(CERTINFO_UNIQUEIDENTIFIER)
  ENUM(CERTINFO_COUNTRYNAME)
  ENUM(CERTINFO_STATEORPROVINCENAME)
  ENUM(CERTINFO_ORGANIZATIONNAME)
  ENUM(CERTINFO_ORGANISATIONNAME)
  ENUM(CERTINFO_ORGANIZATIONALUNITNAME)
  ENUM(CERTINFO_ORGANISATIONALUNITNAME)
  ENUM(CERTINFO_COMMONNAME)
  ENUM(CERTINFO_OTHERNAME)
  ENUM(CERTINFO_RFC822NAME)
  ENUM(CERTINFO_DNSNAME)
  ENUM(CERTINFO_X400ADDRESS)
  ENUM(CERTINFO_DIRECTORYNAME)
  ENUM(CERTINFO_EDIPARTYNAME)
  ENUM(CERTINFO_UNIFORMRESOURCEIDENTIFIER)
  ENUM(CERTINFO_IPADDRESS)
  ENUM(CERTINFO_REGISTEREDID)
  ENUM(CERTINFO_ACCOUNTALIAS)
  ENUM(CERTINFO_HASHEDROOTKEY)
  ENUM(CERTINFO_CERTIFICATETYPE)
  ENUM(CERTINFO_MERCHANTDATA)
  ENUM(CERTINFO_LAST)
  ENUM(OPTION_NONE)
  ENUM(OPTION_ENCR_ALGO)
  ENUM(OPTION_ENCR_MODE)
  ENUM(OPTION_ENCR_HASH)
  ENUM(OPTION_ENCR_COOKIES)
  ENUM(OPTION_PKC_ALGO)
  ENUM(OPTION_PKC_KEYSIZE)
  ENUM(OPTION_SIG_ALGO)
  ENUM(OPTION_SIG_KEYSIZE)
  ENUM(OPTION_KEYING_ALGO)
  ENUM(OPTION_KEYING_ITERATIONS)
  ENUM(OPTION_KEYS_PUBLIC)
  ENUM(OPTION_KEYS_PRIVATE)
  ENUM(OPTION_KEYS_SIGCHECK)
  ENUM(OPTION_KEYS_SIGNATURE)
  ENUM(OPTION_KEYS_PGP_PUBLIC)
  ENUM(OPTION_KEYS_PGP_PRIVATE)
  ENUM(OPTION_KEYS_PGP_SIGCHECK)
  ENUM(OPTION_KEYS_PGP_SIGNATURE)
  ENUM(OPTION_KEYS_DBMS_NAMETABLE)
  ENUM(OPTION_KEYS_DBMS_NAMENAME)
  ENUM(OPTION_KEYS_DBMS_NAMEEMAIL)
  ENUM(OPTION_KEYS_DBMS_NAMEDATE)
  ENUM(OPTION_KEYS_DBMS_NAMENAMEID)
  ENUM(OPTION_KEYS_DBMS_NAMEISSUERID)
  ENUM(OPTION_KEYS_DBMS_NAMEKEYID)
  ENUM(OPTION_KEYS_DBMS_NAMEKEYDATA)
  ENUM(OPTION_HW_SERIALRNG)
  ENUM(OPTION_HW_SERIALRNG_PARAMS)
  ENUM(OPTION_HW_SERIALRNG_ONLY)
  ENUM(OPTION_MISC_FORCELOCK)
  ENUM(OPTION_MISC_ENVTYPE)
  ENUM(OPTION_LAST)
  ENUM(MAX_KEYSIZE)
  ENUM(MAX_IVSIZE)
  ENUM(MAX_PKCSIZE)
  ENUM(MAX_HASHSIZE)
  ENUM(MAX_TEXTSIZE)
  ENUM(USE_DEFAULT)
  ENUM(UNUSED)
  ENUM(COMPONENTS_BIGENDIAN)
  ENUM(COMPONENTS_LITTLENDIAN)
  ENUM(KEYTYPE_PRIVATE)
  ENUM(KEYTYPE_PUBLIC)
  ENUM(RANDOM_FASTPOLL)
  ENUM(RANDOM_SLOWPOLL)
  ENUM(KEYSET_GETFIRST)
  ENUM(KEYSET_GETNEXT)
  ENUM(KEYUPDATE_BEGIN)
  ENUM(KEYUPDATE_END)
  ENUM(KEYOPT_NONE)
  ENUM(KEYOPT_READONLY)
  ENUM(KEYOPT_CREATE)
  ENUM(KEYOPT_LAST)
  {0, 0}
};



     /************************/
     /* cryptlib definitions */
     /************************/

/*** ENVELOPE SMOB ***/

#define MAKE_ENVELOPE(env) cons(tc16_envelope, (env))

#define ENVELOPE_P(obj) (NIMP(obj) && (TYP16(obj) == tc16_envelope))

#define GET_ENVELOPE(obj) ((CRYPT_ENVELOPE)CDR(obj))

#if 1
#define STATUS2SCM(st) long2num(st)
#else
#define STATUS2SCM(st) (cryptStatusOK(st) ? BOOL_T : BOOL_F)
#endif

#define IF_OK(st, expr) ((cryptStatusOK(st) ? (expr) : STATUS2SCM(st)))

int tc16_envelope;

/* envelope smob functions */

/* envelope -- smob.print */
int prinenvlp(SCM exp, SCM port, int writing)
{
  writing = writing;
  lputs("#<Envelope ", port);
  intprint(CDR(exp), 10, port);
  lputs(">", port);
  return 1;
}

/*** CONTEXT SMOB ***/

#define MAKE_CONTEXT(ctxt) cons(tc16_context, ctxt)

#define CONTEXT_P(obj) (NIMP(obj) && (TYP16(obj) == tc16_context))

#define GET_CONTEXT(obj) ((CRYPT_CONTEXT)CDR(obj))
int tc16_context;

/* context smob functions */

/* context -- smob.print */
int princntxt(SCM exp, SCM port, int writing)
{
  writing = writing;
  lputs("#<Context ", port);
  intprint(CDR(exp), 10, port);
  lputs(">", port);
  return 1;
}

     /**************************/
     /* SCM support procedures */
     /**************************/

#ifndef HAVE_STRCASECMP
#define strcasecmp strcmp
#define strncasecmp strncmp
#endif

/*** "cryptlib:lookup" ***/
/* Will lookup corresponding value of enum characterized by string parameter */
static char s_cryptLookupDef[] = MOD "lookup";
static SCM p_cryptLookupDef(SCM lookupstr, SCM prefix)
{
  int i, j, k;
  char *matchstr, *pfx, *p;
  size_t lengths[10];

  ASSERT(STRINGP(lookupstr), lookupstr, ARG1, s_cryptLookupDef);
  ASSERT(STRINGP(prefix)||UNBNDP(prefix), prefix, ARG2, s_cryptLookupDef);

  if (STRINGP(prefix))
    pfx = CHARS(prefix);
  else
    pfx = "CRYPT_";

  lengths[0] = 0;
  for (i = 0; i < 8; ++i) {
    p = strchr(pfx + lengths[i], '_');
    if (p == NULL)
      break;
    else
      lengths[i+1] = p + 1 - pfx;
  }
  lengths[i+1] = strlen(pfx);

  matchstr = mak0strfrom(lookupstr);

  for (j = 0; j < i+2; ++j) {
    for (k = 0; cryptEnums[k].name; ++k) {
      if ((strncasecmp(cryptEnums[k].name, pfx, lengths[j]) == 0)
	  && (strcasecmp(cryptEnums[k].name + lengths[j], matchstr) == 0)) {
	return(long2num(cryptEnums[k].value));
      }
    }
  }
  return UNDEFINED;
}

/*** "cryptlib:initialize!" ***/
static char s_cryptInit[] = MOD "initialize!";
static SCM p_cryptInit()
{
  CRET intret;
  
  intret = cryptInit();
  return STATUS2SCM(intret);
}

/*** "cryptlib:end!" ***/
static char s_cryptEnd[] = MOD "end!";
static SCM p_cryptEnd()
{
  CRET intret;
  
  intret = cryptEnd();
  return STATUS2SCM(intret);
}


/*** "cryptlib:create-envelope" ***/
static char s_cryptCreateEnvelope[] = MOD "create-envelope";
static SCM p_cryptCreateEnvelope()
{
  CRET intret;
  CRYPT_ENVELOPE c_env;
  SCM e;

  intret = cryptCreateEnvelope(&c_env);
  return IF_OK(intret, MAKE_ENVELOPE(c_env));
}

/*** "cryptlib:create-decryption-envelope" ***/
static char s_cryptCreateDeenvelope[] = MOD "create-decryption-envelope";
static SCM p_cryptCreateDeenvelope()
{
  CRET intret;
  CRYPT_ENVELOPE c_env;

  intret = cryptCreateDeenvelope(&c_env);
  return IF_OK(intret, MAKE_ENVELOPE(c_env));
}

/*** "cryptlib:destroy-envelope!" ***/
static char s_cryptDestroyEnvelope[] = MOD "destroy-envelope!";
static SCM p_cryptDestroyEnvelope(SCM envelope)
{
  CRET intret;
  
  ASSERT(ENVELOPE_P(envelope), envelope, ARG1, s_cryptDestroyEnvelope);
  intret = cryptDestroyEnvelope(GET_ENVELOPE(envelope));
  return STATUS2SCM(intret);
}

/*** "cryptlib:add-resource!" ***/
static char s_cryptAddResource[] = MOD "add-resource!";
static SCM p_cryptAddResource(SCM envelope, SCM whatres, SCM theres)
/* envelope: envelope smob
   whatres: CRYPT_RESOURCE_TYPE (int), or string naming one.
   theres: string
   */
{
  CRET intret;
  char *stringres;

  ASSERT(ENVELOPE_P(envelope), envelope, ARG1, s_cryptAddResource);
  if (STRINGP(whatres))
    whatres = p_cryptLookupDef(whatres, makfrom0str("CRYPT_RESOURCE_"));
  ASSERT(NUMBERP(whatres), whatres, ARG2, s_cryptAddResource);
  ASSERT(STRINGP(theres), theres, ARG3, s_cryptAddResource);

  intret = cryptAddResource(GET_ENVELOPE(envelope), num2long(whatres),
			    mak0strfrom(theres));

  return STATUS2SCM(intret);
}

/*** "cryptlib:push-data!" ***/
static char s_cryptPushData[] = MOD "push-data!";
static SCM p_cryptPushData(SCM envelope, SCM datain)
     /* envelope: envelope smob
	datain: string
	returns: # of bytes copied if successful, error code if not
	*/
{
  CRET intret;
  char *stringdatain;
  int datacopied;

  ASSERT(ENVELOPE_P(envelope), envelope, ARG1, s_cryptPushData);
  ASSERT(STRINGP(datain), datain, ARG2, s_cryptPushData);

  intret = cryptPushData(GET_ENVELOPE(envelope), CHARS(datain),
			 LENGTH(datain), &datacopied);
  return IF_OK(intret, long2num(datacopied));
}

/*** "cryptlib:pop-data!" ***/
static char s_cryptPopData[] = MOD "pop-data!";
static SCM p_cryptPopData(SCM envelope, SCM dataoutsize)
     /* envelope: envelope smob
	dataoutsize: int
	returns: data as string if successful, error code if not
	*/
{
  CRET intret;
  char *dataout;
  int datacopied;
  SCM ret;

  ASSERT(ENVELOPE_P(envelope), envelope, ARG1, s_cryptPopData);
  ASSERT(NUMBERP(dataoutsize), dataoutsize, ARG2, s_cryptPopData);

  dataout = malloc(dataoutsize);
  if (dataout == NULL) {
    wta(dataoutsize, NALLOC, s_cryptPopData);
    return BOOL_F;
  }

  intret = cryptPopData(GET_ENVELOPE(envelope), dataout, dataoutsize,
			&datacopied);

  ret = IF_OK(intret, makfromstr(dataout, datacopied));
  free(dataout);

#if 0
  printf("cryptPopData: popped %d bytes\n", datacopied);
#endif
  return ret;
}

/*** "cryptlib:envelope-size" ***/
static char s_cryptEnvelopeSize[] = MOD "envelope-size";
static SCM p_cryptEnvelopeSize(SCM envelope)
{
  int intret;
  int envsize;

  ASSERT(ENVELOPE_P(envelope), envelope, ARG1, s_cryptEnvelopeSize);

  intret = cryptEnvelopeSize(GET_ENVELOPE(envelope), &envsize);

  return IF_OK(intret, long2num(envsize));
}

/*** "cryptlib:add-random-data!" ***/
static char s_cryptAddRandom[] = MOD "add-random-data!";
static SCM p_cryptAddRandom(SCM buffer)
{
  CRET intret;
  char *randstring;

  if (buffer == BOOL_T)
    intret = cryptAddRandom(NULL, CRYPT_RANDOM_SLOWPOLL);
  else if (buffer == BOOL_F)
    intret = cryptAddRandom(NULL, CRYPT_RANDOM_FASTPOLL);
  else {
    ASSERT(STRINGP(buffer), buffer, ARG1, s_cryptAddRandom);    
    intret = cryptAddRandom(CHARS(buffer), LENGTH(buffer));
  }
    
  return STATUS2SCM(intret);
}

/*** "cryptlib:random-data" ***/
static char s_cryptGetRandom[] = MOD "random-data";
static SCM p_cryptGetRandom(SCM buffer_length)
{
  char *randstring;
  CRET intret;
  int l;

  ASSERT(NUMBERP(buffer_length), buffer_length, ARG1, s_cryptGetRandom);
  l = num2long(buffer_length);
  randstring = malloc(l);
  if (randstring == NULL) {
    wta(buffer_length, NALLOC, s_cryptGetRandom);
    return BOOL_F;
  }

  intret = cryptGetRandom(randstring, l);

  return IF_OK(intret, makfromstr(randstring, l));
}

/*** "cryptlib:create-context" ***/
static char s_cryptCreateContext[] = MOD "create-context";
static SCM p_cryptCreateContext(SCM algo, SCM mode)
{
  CRYPT_CONTEXT c_ctxt;

  if (STRINGP(algo))
    algo = p_cryptLookupDef(algo, makfrom0str("CRYPT_ALGO_"));
  ASSERT(NUMBERP(algo), algo, ARG1, s_cryptCreateContext);
  if (STRINGP(mode))
    mode = p_cryptLookupDef(mode, makfrom0str("CRYPT_MODE_"));
  ASSERT(NUMBERP(mode), mode, ARG2, s_cryptCreateContext);

  cryptCreateContext(&c_ctxt, long2num(algo), long2num(mode));
  return MAKE_CONTEXT(c_ctxt);
}

/*** "cryptlib:destroy-context!" ***/
static char s_cryptDestroyContext[] = MOD "destroy-context!";
static SCM p_cryptDestroyContext(SCM context)
{
  CRET intret;
  
  ASSERT(CONTEXT_P(context), context, ARG1, s_cryptDestroyContext);
  
  intret = cryptDestroyContext(GET_CONTEXT(context));

  return STATUS2SCM(intret);
}

/*** "cryptlib:generate-key!" ***/
static char s_cryptGenerateKey[] = MOD "generate-key!";
static SCM p_cryptGenerateKey(SCM context, SCM keylength)
     /* keylength is optional. */
{
  CRET intret;
  
  ASSERT(CONTEXT_P(context), context, ARG1, s_cryptGenerateKey);
  ASSERT(UNBNDP(keylength)||NUMBERP(keylength), keylength, ARG2,
	 s_cryptGenerateKey);
  
  if (UNBNDP(keylength))
    intret = cryptGenerateKey(GET_CONTEXT(context));
  else
    intret = cryptGenerateKeyEx(GET_CONTEXT(context), num2long(keylength));

  return STATUS2SCM(intret);
}

/*** "cryptlib:derive-key!" ***/
static char s_cryptDeriveKey[] = MOD "derive-key!";
static SCM p_cryptDeriveKey(SCM context, SCM userkey, SCM algo, SCM iter)
     /* context: context smob
	userkey: string
	algo: optional CRYPT_ALGO (int)
	iter: optional int
	*/
{
  CRET intret;

  ASSERT(CONTEXT_P(context), context, ARG1, s_cryptDeriveKey);
  ASSERT(STRINGP(userkey), userkey, ARG2, s_cryptDeriveKey);
  if (STRINGP(algo))
    algo = p_cryptLookupDef(algo, makfrom0str("CRYPT_ALGO_"));
  ASSERT(UNBNDP(algo) || NUMBERP(algo), algo, ARG3, s_cryptCreateContext);
  ASSERT(UNBNDP(iter) || NUMBERP(iter), iter, ARG4, s_cryptCreateContext);

  if (UNBNDP(iter))
    intret = cryptDeriveKey(GET_CONTEXT(context), CHARS(userkey),
			    LENGTH(userkey));
  else
    intret = cryptDeriveKeyEx(GET_CONTEXT(context), CHARS(userkey),
			      LENGTH(userkey), num2long(algo), num2long(iter));

  return STATUS2SCM(intret);
}

/*** "cryptlib:load-key!" ***/
static char s_cryptLoadKey[] = MOD "load-key!";
static SCM p_cryptLoadKey(SCM context, SCM key)
     /* context: context smob
	key: string
	*/
{
  CRET intret;

  ASSERT(CONTEXT_P(context), context, ARG1, s_cryptLoadKey);
  ASSERT(STRINGP(key), key, ARG2, s_cryptLoadKey);

  intret = cryptLoadKey(GET_CONTEXT(context), CHARS(key), LENGTH(key));

  return STATUS2SCM(intret);
}

/*** "cryptlib:load-iv!" ***/
static char s_cryptLoadIV[] = MOD "load-iv!";
static SCM p_cryptLoadIV(SCM context, SCM iv)
     /* context: context smob
	iv: string
	*/
{
  CRET intret;

  ASSERT(CONTEXT_P(context), context, ARG1, s_cryptLoadIV);
  ASSERT(STRINGP(iv), iv, ARG2, s_cryptLoadIV);

  intret = cryptLoadIV(GET_CONTEXT(context), CHARS(iv), LENGTH(iv));
  return STATUS2SCM(intret);
}

/*** "cryptlib:iv" ***/
static char s_cryptRetrieveIV[] = MOD "iv";
static SCM p_cryptRetrieveIV(SCM context)
     /* context: context smob
	returns: string if successful, error code if not
	*/
{
  CRET intret;
  char *iv, *iv2;
  int len;

  ASSERT(CONTEXT_P(context), context, ARG1, s_cryptRetrieveIV);
  iv = malloc(CRYPT_MAX_IVSIZE);
  if (iv == NULL) {
    wta(BOOL_F, NALLOC, s_cryptRetrieveIV);
    return BOOL_F;
  }
  iv2 = malloc(CRYPT_MAX_IVSIZE);
  if (iv2 == NULL) {
    wta(BOOL_F, NALLOC, s_cryptRetrieveIV);
    free(iv);
    return BOOL_F;
  }
  
  /* Hack to determine the length of the iv.  Sigh. */
  memset(iv, 0, CRYPT_MAX_IVSIZE);
  memset(iv2, 1, CRYPT_MAX_IVSIZE);

  intret = cryptRetrieveIV(GET_CONTEXT(context), iv);
  if (cryptStatusOK(intret))
    cryptRetrieveIV(GET_CONTEXT(context), iv2);
  for (len = 0; len < CRYPT_MAX_IVSIZE; ++len)
    if (iv[len] != iv2[len])
      break;

  return IF_OK(intret, makfromstr(iv, len));
}

/****************************/
/* envelope smob definition */
/****************************/

static smobfuns envlp =
{
  mark0,    /* no storage allocated to mark */
  free0,    /* or free */
  prinenvlp, /* custom print */
  0         /* no two envelopes can be the same */
};

/***************************/
/* context smob definition */
/***************************/

static smobfuns cntxt =
{
  mark0,    /* no storage allocated to mark */
  free0,    /* or free */
  princntxt, /* custom print */
  0         /* no two contexts can be the same */
};

/***********************/
/* SCM procedure lists */
/***********************/

#define PROCIFY(pname) {s_ ## pname, p_ ## pname},
#define DONE {0, 0} 

static iproc procs0[] =
{
  PROCIFY(cryptInit)
  PROCIFY(cryptEnd)
  PROCIFY(cryptCreateEnvelope)
  PROCIFY(cryptCreateDeenvelope)
  DONE
};

static iproc procs1[] =
{
  PROCIFY(cryptDestroyEnvelope)
  PROCIFY(cryptEnvelopeSize)
  PROCIFY(cryptAddRandom)
  PROCIFY(cryptGetRandom)
  PROCIFY(cryptDestroyContext)
  PROCIFY(cryptRetrieveIV)
  DONE
};

static iproc procs2o[] =
{
  PROCIFY(cryptLookupDef)
  PROCIFY(cryptGenerateKey)
  DONE
};

static iproc procs2[] =
{
  PROCIFY(cryptPushData)
  PROCIFY(cryptPopData)
  PROCIFY(cryptCreateContext)
  PROCIFY(cryptLoadKey)
  PROCIFY(cryptLoadIV)
  DONE
};


static iproc procs3[] =
{
  PROCIFY(cryptAddResource)
  DONE
};

void init_cryptlib()
{
  tc16_envelope = newsmob(&envlp);
  tc16_context = newsmob(&cntxt);
  
  init_iprocs(procs0, tc7_subr_0);
  init_iprocs(procs1, tc7_subr_1);
  init_iprocs(procs2o, tc7_subr_2o);
  init_iprocs(procs2, tc7_subr_2);
  init_iprocs(procs3, tc7_subr_3);

  make_gsubr(s_cryptDeriveKey, 2, 2, 0, p_cryptDeriveKey);

  add_feature("cryptlib");
}


