OME checksum()

From Ingres Community Wiki

Jump to: navigation, search

Contents

Introduction

The checksum() function uses the openssl encryption routines to provide an md5sum (or md2sum or sha1sum) of an input string.

Written by Martin Bowes.

Syntax

checksum(
    (varchar ) digest,
    (varchar | varbyte | long varchar | long byte) string
    )

The digest must be known to openssl. Currently use one of 'md5', 'md2' or 'sha1'.

Return Values

The function will return a varchar sized according to the digest chosen. It defaults to a varchar(50).

Examples

select checksum('md5', 'Hello World!' + x'0a')\g

returns 8ddd8be4b179a529afa5f2ffae4b9858

Note that the x'0a' was used to terminate the string as per a UNIX file. This allows simple comparison with an md5sum generated on a file containing the string 'Hello World!'.

FOD

Include the following definition in the fod_id enum set: UDF_CHECKSUM 

The install the following function definition:

static IIADD_FO_DFN Function_Definitions[]={
    ...
   {/* Define new function "checksum"*/
   II_O_OPERATION,   /*fod_object_type*/
   {"checksum"},     /*fod_name*/
   UDF_CHECKSUM,     /*fod_id*/
   II_NORMAL         /*fod_type*/
   },
};

FIDs

The following definitions should be included in the fid_id enum set:

UDF_FI_CHECKSUM_VARCHAR,
UDF_FI_CHECKSUM_VARBYTE,
UDF_FI_CHECKSUM_LVARCHAR,
UDF_FI_CHECKSUM_LVARBYTE

The FIDs rely on the following datatype arrays.

static II_DT_ID  UD_2_VC[]         = {II_VARCHAR,  II_VARCHAR};
static II_DT_ID  UD_VC_N_VB[]      = {II_VARCHAR,  II_VBYTE};
static II_DT_ID  UD_VC_N_LVC[]     = {II_VARCHAR,  II_LVCH};
static II_DT_ID  UD_VC_N_LB[]      = {II_VARCHAR,  II_LBYTE};

The FIDs are...

static IIADD_FI_DFN Function_Instances[] = {
   {/* checksum(varchar, varchar) */
   II_O_FUNCTION_INSTANCE,    /* fid_object_type */
   UDF_FI_CHECKSUM_VARCHAR,   /* fid_id*/
   II_NO_FI,                  /* fid_cmplmnt*/
   UDF_CHECKSUM,              /* fid_opid=fod_id from function definition
                              ** This is the minor sort field for this array
                              */
   II_NORMAL,                 /* fid_optype
                              ** This is the major sort field for this array
                              */
   II_FID_F0_NOFLAGS,         /* fid_attributes*/
   0,                         /* fid_wslength*/
   2,                         /* fid_numargs*/
   UD_2_VC,                   /* fid_args, a pointer to an array of datatypes*/
   II_VARCHAR,                /* fid_result, result is an integer*/
   II_RES_EXTERN,             /* fid_rltype*/
   II_LEN_UNKNOWN,            /* fid_rlength */
   0,                         /* fid_rprec */
   checksum,                  /* fid_routine */
   checksum_ls                /* lenspec_routine */
   }, /* checksum(varchar, varchar) */

   {/* checksum(varchar, varbyte) */
   II_O_FUNCTION_INSTANCE,    /* fid_object_type */
   UDF_FI_CHECKSUM_VARBYTE,   /* fid_id*/
   II_NO_FI,                  /* fid_cmplmnt*/
   UDF_CHECKSUM,              /* fid_opid=fod_id from function definition
                              ** This is the minor sort field for this array
                              */
   II_NORMAL,                 /* fid_optype
                              ** This is the major sort field for this array
                              */
   II_FID_F0_NOFLAGS,         /* fid_attributes*/
   0,                         /* fid_wslength*/
   2,                         /* fid_numargs*/
   UD_VC_N_VB,                /* fid_args, a pointer to an array of datatypes*/
   II_VARCHAR,                /* fid_result, result is an integer*/
   II_RES_EXTERN,             /* fid_rltype*/
   II_LEN_UNKNOWN,            /* fid_rlength */
   0,                         /* fid_rprec */
   checksum,                  /* fid_routine */
   checksum_ls                /* lenspec_routine */
   }, /* checksum(varchar, varbyte) */
  
   {/* checksum(varchar, long varchar) */
   II_O_FUNCTION_INSTANCE,    /* fid_object_type */
   UDF_FI_CHECKSUM_LVARCHAR,  /* fid_id*/
   II_NO_FI,                  /* fid_cmplmnt*/
   UDF_CHECKSUM,              /* fid_opid=fod_id from function definition
                              ** This is the minor sort field for this array
                              */
   II_NORMAL,                 /* fid_optype
                              ** This is the major sort field for this array
                              */
   II_FID_F0_NOFLAGS,         /* fid_attributes*/
   0,                         /* fid_wslength*/
   2,                         /* fid_numargs*/
   UD_VC_N_LVC,               /* fid_args, a pointer to an array of datatypes*/
   II_VARCHAR,                /* fid_result, result is an integer*/
   II_RES_EXTERN,             /* fid_rltype*/
   II_LEN_UNKNOWN,            /* fid_rlength */
   0,                         /* fid_rprec */
   long_checksum,             /* fid_routine */
   checksum_ls                /* lenspec_routine */
   }, /* checksum(varchar, long varchar) */

   {/* checksum(varchar, long byte) */
   II_O_FUNCTION_INSTANCE,    /* fid_object_type */
   UDF_FI_CHECKSUM_LVARBYTE,  /* fid_id*/
   II_NO_FI,                  /* fid_cmplmnt*/
   UDF_CHECKSUM,              /* fid_opid=fod_id from function definition
                              ** This is the minor sort field for this array
                              */
   II_NORMAL,                 /* fid_optype
                              ** This is the major sort field for this array
                              */
   II_FID_F0_NOFLAGS,         /* fid_attributes*/
   0,                         /* fid_wslength*/
   2,                         /* fid_numargs*/
   UD_VC_N_LB,                /* fid_args, a pointer to an array of datatypes*/
   II_VARCHAR,                /* fid_result, result is an integer*/
   II_RES_EXTERN,             /* fid_rltype*/
   II_LEN_UNKNOWN,            /* fid_rlength */
   0,                         /* fid_rprec */
   long_checksum,             /* fid_routine */
   checksum_ls                /* lenspec_routine */
   }, /* checksum(varchar, long byte) */
}

Executor Code

You will need...

#include <openssl/evp.h>    /* Required for checksum(), encrypt(), decrypt() */
/* max string length returned by checksum */
#define MAX_CHECKSUM_LENGTH 50

checksum(digest, (varchar | varbyte) string)

II_STATUS
checksum (
   II_SCB          *scb,
   II_DATA_VALUE   *p1, /* digest_name  */
   II_DATA_VALUE   *p2, /* text to hash */
   II_DATA_VALUE   *rdv /* return_text  */
   )
{   
   EVP_MD_CTX mdctx;
   const EVP_MD *md;
   unsigned char md_value[EVP_MAX_MD_SIZE];
   char digest[33], xx[3], msg[128];
   unsigned short true_length;
   int md_len, i; 
    
   rdv->db_prec     = 0;

   OpenSSL_add_all_digests();
   true_length=*(short* )p1->db_data;
   if (true_length > 32) {
       strncpy(digest, (char *)p1->db_data + sizeof(short), 32);
       digest[32]='\0';
       sprintf(msg,
           "checksum(): digest name commencing '%s' is too long to be valid!",
            digest);
       us_error(scb, 0x200010, msg);
       return(II_ERROR);
       };

   /*
   ** Confirm p1 is a known digest
   */
   strncpy(digest, (char *)p1->db_data + sizeof(short), true_length);
   digest[true_length]='\0';

   md = EVP_get_digestbyname(digest);

   if(!md) {
       sprintf(msg, "checksum(): Unknown digest '%s'",
            digest);
       us_error(scb, 0x200011, msg);
       return(II_ERROR);
       };

   /*
   ** The 2nd parameter is passed in a 'varchar' like format regardless of
   ** its true data type. Hence allow for a two character length offset at the
   ** start of the data
   */
   true_length=*(short *)p2->db_data;
   EVP_DigestInit(&mdctx, md);
   EVP_DigestUpdate(&mdctx, (char *)p2->db_data + sizeof(short), true_length);
   EVP_DigestFinal(&mdctx, md_value, &md_len);

   /* Check return length is sane */
   if (md_len * 2 > MAX_CHECKSUM_LENGTH) {
       sprintf(msg,
           "checksum(): Return length of %d exceeds maximumm permitted (%d)",
           md_len * 2, MAX_CHECKSUM_LENGTH
           );
       us_error(scb, 0x200011, msg);
       return(II_ERROR);
   };

   /* And fill in the data...
   ** Note that this is a direct rip-off of the man page...
   */
   *((short *) rdv->db_data)=md_len * 2;
   sprintf(msg,"Result (type %d, length %d): ", rdv->db_datatype, *((short *) rdv->db_data));
   for(i = 0; i < md_len; i++) {
       sprintf(xx, "%02x", md_value[i]);
       memcpy((char *)(rdv->db_data + sizeof(short) + (i * 2)), xx, 2);
       if (env_sym) { xx[2]='\0'; strcat(msg,xx); };
   };
   return(II_OK);
}; /* checksum */

checksum(digest, (long varchar | long byte) string)

II_STATUS
long_checksum (
   II_SCB          *scb,
   II_DATA_VALUE   *p1, /* digest_name  */
   II_DATA_VALUE   *p2, /* text to hash */
   II_DATA_VALUE   *rdv /* return_text  */
   ) 
{
   EVP_MD_CTX      mdctx;
   const EVP_MD    *md;
   unsigned char   md_value[EVP_MAX_MD_SIZE];
   char            digest[33], xx[3], msg[128], segspace[2048];
   unsigned short  true_length; 
   int             md_len, i;
   II_STATUS       status;
   II_POP_CB       pop_cb;
   II_DATA_VALUE   underdv, segment, coupon;

   status=II_OK;

   /* Set coupon to be a copy of p2 */
   coupon.db_data     = p2->db_data;
   coupon.db_length   = p2->db_length;
   coupon.db_datatype = p2->db_datatype;
   coupon.db_prec     = 0;

   /* Initiialise parts of the segment */
   segment.db_data     = NULL;
   segment.db_length   = 0;
   segment.db_datatype = p2->db_datatype;
   segment.db_prec     = 0;

   rdv->db_prec     = 0;

   OpenSSL_add_all_digests();
   true_length=*(short* )p1->db_data;
   if (true_length > 32) {
       strncpy(digest, (char *)p1->db_data + sizeof(short), 32);
       digest[32]='\0';
       sprintf(msg,
           "checksum(): digest name commencing '%s' is too long to be valid!",
            digest);
       us_error(scb, 0x200010, msg);
       return(II_ERROR);
       };

   /*
   ** Confirm p1 is a known digest
   */
   strncpy(digest, (char *)p1->db_data + sizeof(short), true_length);
   digest[true_length]='\0';

   md = EVP_get_digestbyname(digest);

   if(!md) {
       sprintf(msg, "checksum(): Unknown digest '%s'", digest);
       us_error(scb, 0x200011, msg);
       return(II_ERROR);
       };

   /*
   ** The 2nd parameter is a long. This will have to be processed in
   ** segments. Each segment is separately processed via the EVP_DigestUpdate.
   */
   /* Initialise the pop_cb, want to act on p2 */
   pop_cb.pop_length               = sizeof(pop_cb);
   pop_cb.pop_type                 = II_POP_TYPE;
   pop_cb.pop_ascii_id             = 0;
   pop_cb.pop_temporary            = II_POP_SHORT_TEMP;

   pop_cb.pop_underdv  = &underdv;
   underdv.db_datatype = coupon.db_datatype;
   underdv.db_data     = NULL;
   underdv.db_length   = 0;

   pop_cb.pop_coupon   = &coupon;

   pop_cb.pop_segment              = &segment;

   /* Determine the size of the segments that may be used */
   status=(*usc_lo_handler)(II_INFORMATION, &pop_cb);
   if (status) {
       sprintf(msg,
           "checksum(): Error %d encountered seeking INFORMATION on long segment length\n",
           pop_cb.pop_error.err_code);
       us_error(scb, 0x200011, msg);
       return(status);
   };

   /* Now set to read that many bytes into a (II_DATA_VALUE )segment */
   segment.db_length    = underdv.db_length;
   segment.db_datatype  = underdv.db_datatype;
   segment.db_prec      = underdv.db_prec;
   segment.db_data      = segspace;

   pop_cb.pop_continuation = II_C_BEGIN_MASK;

   EVP_DigestInit(&mdctx, md); /* Initialise the digest */
   do {
       status=(*usc_lo_handler)(II_GET, &pop_cb);
       if (status)
       {
           if ((status >= II_ERROR)
           || (pop_cb.pop_error.err_code != II_E_NOMORE))
           {
               sprintf(msg, "checksum(): Unexpected error %d encountered processing long object\n", status);
               us_error(scb, 0x200010, msg);
               return(II_ERROR);
           };
       };
       /* Got a Segment */
       pop_cb.pop_continuation = 0;
       true_length=*(short *)segment.db_data;
       EVP_DigestUpdate(&mdctx, (char *)segment.db_data + sizeof(short), true_length);
   } while ((status <= II_ERROR)
           && (pop_cb.pop_error.err_code != II_E_NOMORE));

   EVP_DigestFinal(&mdctx, md_value, &md_len);

   /* Check return length is sane */
   if (md_len * 2 > MAX_CHECKSUM_LENGTH) {
       sprintf(msg,
           "checksum(): Return length of %d exceeds maximumm permitted (%d)",
           md_len * 2, MAX_CHECKSUM_LENGTH
           );
       us_error(scb, 0x200011, msg);
       return(II_ERROR);
   };

   /* And fill in the data...
   ** Note that this is a direct rip-off of the man page...
   */
   *((short *) rdv->db_data)=md_len * 2;
   strcpy((char *)(rdv->db_data + sizeof(short)), "");
   sprintf(msg,"Result (type %d, length %d): ", rdv->db_datatype, *((short *) rdv->db_data));
   for(i = 0; i < md_len; i++) {
       sprintf(xx, "%02x", md_value[i]);
       xx[2]='\0';
       strcat((char *)(rdv->db_data + sizeof(short)), xx);
       strcat(msg,xx); 
       };
   };
   return(II_OK);
}; /* long_checksum */

Lenspec Routine

The following routine is used to calculate the return length. Note that wherever it feels unable to calulate a length it returns the default specified by MAX_CHECKSUM_LENGTH. Furthermore all lengths returned have been incremented by sizeof(short) to allow for the length specifier associated with varchar data.

II_STATUS
checksum_ls (
   II_SCB          *scb, 
   II_DT_ID        *opid,
   II_DATA_VALUE   *p1, /* digest_name  */
   II_DATA_VALUE   *p2, /* text to hash */
   II_DATA_VALUE   *rdv /* return_text  */ 
   )
{   
   char digest[33]; /*more than long enough*/
   unsigned short true_length;

   if (!p1->db_data) {
       /* This occurs if the digest name is not an absolute string but a column */
       rdv->db_length=MAX_CHECKSUM_LENGTH + sizeof(short);
       return(II_OK);
       };

   /* p1 is an absolute string */
   true_length=*(short* )p1->db_data;
   if (true_length > 32) {
       strncpy(digest, (char *)p1->db_data + sizeof(short), 32);
       digest[32]='\0';
       }
   else {
       strncpy(digest, (char *)p1->db_data + sizeof(short), true_length);
       digest[true_length]='\0';
       }; 

   if ((!strcmp(digest, "md2")) || (!strcmp(digest, "md5"))) {
       rdv->db_length=32 + sizeof(short);
       }
   else if (!strcmp(digest, "sha1")) {
       rdv->db_length=40 + sizeof(short);
       }
   else {rdv->db_length=MAX_CHECKSUM_LENGTH + sizeof(short);};
   return(II_OK);
}; /*checksum_ls*/ 

Linking Instructions

If iilink fails to link with the openssl libraries, edit the $II_SYSTEM/ingres/utility/iisysdep and extend the shell variable LDLIBMACH to include -lssl.

You will have to ensure this edit is done after each subsequent patch install.

Personal tools
Developing With