OME checksum()
From Ingres Community Wiki
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.
